LockSupport和Object中wait/notify的异同点

先说结论

共同点
  • LockSupport中的park方法和Object中的wait方法都可以使线程进入WAIT或者TIMED_WAIT状态
  • LockSupport中的unpark方法和Object中的notify可以使线程脱离WAITTIMED_WAIT状态
  • 二者都可以通过调用线程的interrupt方法脱离等待状态
  • 两者都是通过JVM层,也就是native代码实现的
不同点
  • Object中的wait方法在调用时当前线程必须要对该Object进行加锁,否则会抛出IllegalMonitorException。而LockSupport无需加锁,直接调用其静态方法park就可以使当前线程进入阻塞状态。
  • Objectwaitnotify方法必须要按顺序调用,如果因为线程调度问题导致线程A先调用notify方法而线程B后调用wait方法,那么会使线程A永远处于WAIT状态。对于LockSupport而言则没有这种限制,如果有线程A首先调用了unpark方法并传入了线程B的引用,然后线程B再调用了park方法,那么线程B是不会进入等待状态的。
  • 调用Objectwait方法后,可以调用该线程的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

LockSupportpark方法和unpark方法在JDK1.8中是基于sun.misc.Unsafe实现的,Unsafe直接提供了parkunpark方法,它们都是native方法,并在JVM层实现。

在上文我们对比ObjectLockSupport时,我们提到了LockSupport的唤醒和等待可以乱序调用而不会时线程进入等待状态。这是因为Thread底层的Parker对象维护了一个许可,当首先调用unpark方法时,相当于给了这个线程一个许可,当再次调用park方法时,线程发现已经持有了这个许可后就不会进入等待状态了;如果线程发现没有许可,就会进入等待状态。

Object的wait/notify

在调用一个Object对象的wait方法时必须时当前线程对该对象加锁:

synchronized(obj) {
	obj.wait();
}

wait方法在底层可以分为三个步骤:

  1. 解除当前对象的锁
  2. 进入WAIT状态,等待唤醒(或interrupt中断线程)
  3. 不论是通过notifynotifyAll唤醒线程,还是通过interrupt方法脱离等待状态(即使捕获InterruptedException的逻辑不在同步代码中),都会使该线程重新竞争该对象的锁,在重新得到锁前线程会处于BLOCKED状态。

调用notify/notifyAll方法同样需要获得锁,它会将JVM内部某个变量作一个标记,使线程唤醒。

notify方法是非公平的,会随机唤醒一个正在等待这个锁的线程。类似于AQSCondition,每个Object对象内部都可以持有一个等待队列,这个等待队列可以存放在该对象等待的线程,从而实现Objectwait/notify机制。源码分析可以参考这篇文章:https://www.jianshu.com/p/f4454164c017

你可能感兴趣的:(LockSupport和Object中wait/notify的异同点)