先说结论
共同点
LockSupport
中的park
方法和Object
中的wait
方法都可以使线程进入WAIT
或者TIMED_WAIT
状态LockSupport
中的unpark
方法和Object
中的notify
可以使线程脱离WAIT
、TIMED_WAIT
状态- 二者都可以通过调用线程的
interrupt
方法脱离等待状态 - 两者都是通过JVM层,也就是native代码实现的
不同点
Object
中的wait
方法在调用时当前线程必须要对该Object
进行加锁,否则会抛出IllegalMonitorException
。而LockSupport
无需加锁,直接调用其静态方法park
就可以使当前线程进入阻塞状态。Object
中wait
和notify
方法必须要按顺序调用,如果因为线程调度问题导致线程A先调用notify
方法而线程B后调用wait
方法,那么会使线程A永远处于WAIT
状态。对于LockSupport
而言则没有这种限制,如果有线程A首先调用了unpark
方法并传入了线程B的引用,然后线程B再调用了park
方法,那么线程B是不会进入等待状态的。- 调用
Object
的wait
方法后,可以调用该线程的interrupt
方法脱离等待状态并捕获InterruptedException
。而LockSupport
的并不能捕获InterruptedException
。
下面我们简单介绍一下两者:
LockSupport
LockSupport
定义了以下静态方法:
方法签名 | 作用 |
---|---|
void unpark(Thread thread) | 唤醒指定的线程 |
void park() | 使当前线程进入等待状态,如果之前调用过unpark方法并传入当前线程,那么不会等待 |
void park(Object blocker) | 同park方法,只不过传入一个对象放置于该线程的parkBlocker成员变量 |
void parkNanos(long nanos) | 同park方法,最多使当前线程等待nanos纳秒 |
void parkNanos(Object blocker, long nanos) | 同parkNanos方法,并设置当前线程的parkBlocker成员变量 |
void parkUntil(long deadline) | 同park方法,等待到指定的时间戳(也就是指定一个绝对时间) |
void parkUntil(Object blocker, long deadline) | 同上述parkUntil方法,并设置当前线程的parkBlocker成员变量 |
Object getBlocker(Thread t) | 获取线程t的parkBlocker成员变量 |
上述方法不仅可以使当前线程进入等待状态,还可以设置一个对象赋给线程的parkBlocker
对象,这个对象一般用于问题排查和系统的监控。
需要注意的是,在调用park
方法并传入blocker
对象后,只要该线程一直处于等待状态,都可以通过getBlocker
方法获得该对象,但是当线程脱离上述状态后,就会将parkBlocker
成员变量设为null
。
LockSupport
的park
方法和unpark
方法在JDK1.8中是基于sun.misc.Unsafe
实现的,Unsafe
直接提供了park
和unpark
方法,它们都是native
方法,并在JVM层实现。
在上文我们对比Object
和LockSupport
时,我们提到了LockSupport
的唤醒和等待可以乱序调用而不会时线程进入等待状态。这是因为Thread
底层的Parker
对象维护了一个许可,当首先调用unpark
方法时,相当于给了这个线程一个许可,当再次调用park
方法时,线程发现已经持有了这个许可后就不会进入等待状态了;如果线程发现没有许可,就会进入等待状态。
Object的wait/notify
在调用一个Object
对象的wait
方法时必须时当前线程对该对象加锁:
synchronized(obj) {
obj.wait();
}
wait
方法在底层可以分为三个步骤:
- 解除当前对象的锁
- 进入
WAIT
状态,等待唤醒(或interrupt
中断线程) - 不论是通过
notify
或notifyAll
唤醒线程,还是通过interrupt
方法脱离等待状态(即使捕获InterruptedException
的逻辑不在同步代码中),都会使该线程重新竞争该对象的锁,在重新得到锁前线程会处于BLOCKED
状态。
调用notify
/notifyAll
方法同样需要获得锁,它会将JVM内部某个变量作一个标记,使线程唤醒。
notify
方法是非公平的,会随机唤醒一个正在等待这个锁的线程。类似于AQS
的Condition
,每个Object
对象内部都可以持有一个等待队列,这个等待队列可以存放在该对象等待的线程,从而实现Object
的wait
/notify
机制。源码分析可以参考这篇文章:https://www.jianshu.com/p/f4454164c017