Java并发读书笔记:线程通讯之等待通知机制

在并发编程中,保证线程同步,从而实现线程之间正确通讯,是一个值得考虑的问题。本篇将参考许多著名书籍,学习如何让多个线程之间相互配合,完成咱们指定的任务。面试

固然本文只是学习了一部分线程间通讯的方法,还有一些例如使用Lock和Condition对象,管道输入输出、生产者消费者等内容,咱们以后再作学习。编程

synchronized 与 volatile

synchronized关键字是Java提供的互斥的内置锁,该锁机制不用显式加锁或者释放锁。互斥执行的特性能够确保对整个临界区代码的执行具备原子性,同步机制保证了共享数据在同一个时刻只被一个线程使用并发

回顾如下synchronized的底层实现:学习

咱们能够对下面这段代码进行反编译:javap -v TestData.classthis

public class TestData {
    public static synchronized void m1(){}
    public synchronized void m2(){}
    public static void main(String[] args) {
        synchronized (TestData.class){
        }
    }
}

编译结果以下:
线程

虽然同步方法和代码块的实现细节不一样,可是归根结底:JVM对于方法或者代码块的实现是基于对Monitor对象的进入和退出操做code

以同步代码块举例:对象

  • monitorenter指令被安排到了代码块开始位置,monitorexit被安排到代码块正常结束和异常处
  • 任何对象都有一个monitor与之相关联,当一个monitor被持有以后,它将会出于锁定状态。
  • 当JVM执行到monitorenter指令时,将会尝试去获取当前对象对应的monitor的全部权。
    • 若其余前程已经有monitor的全部权,那么当前线程将会进入同步队列(SynchronizedQueue),陷入阻塞状态(BLOCKED),直到monitor被释放。
    • 若monitor进入数为0,线程能够进入monitor,此时该线程称为monitor的持有者(owner),并计数加一。
    • 若当前线程已经拥有monitor,是容许从新进入该monitor的,此时计数加一。
  • 得到锁,锁计数加一。失去锁,计数减一。计数为0,即为释放锁。释放锁的操做将会唤醒阻塞在同步队列中的的线程,使其从新得到尝试对monitor的获取。

下图源自《Java并发编程得艺术》4-2
blog


新Java内存模型中提供了比锁更加轻量级的通讯机制,它加强了volatile的内存语义,让volatile拥有和锁同样的语义:告知程序任何对volatile修饰变量的访问都要从共享内存中获取,对它的改变必须同步刷新回共享内存,保证了线程对变量访问的可见性

关于volatile的重点学习,以后再作总结。

等待/通知机制

等待/通知相关的方法被定义在java.lang.Object上,这些方法必须由锁对象来调用。同步实例方法为this,静态方法为类对象,代码块的锁是括号里的玩意儿。

这些方法必须须要获取锁对象以后才能调用,也就是必需要在同步块中或同步方法中调用,不然会抛出IllegalMonitorStateException的异常。

等待

wait() : 调用该方法的线程进入WAITING状态,并释放对象的锁,此时当前线程只有被其余线程通知或中断才会返回。

wait(long)wait(long, int):进入TIMED_WAITING状态,释放锁,当前线程有通知或中断会返回,时间到了也会返回。

通知

notify() : 当前线程通知一个在该对象上等待的另外一线程,被唤醒的线程从等待队列(WAITING)被移动到同步队列(BLOCKED)中,意思是被唤醒的线程不会当即执行,须要等当前线程释放锁以后,而且在同步队列中的线程获得了锁才能执行。
notifyAll() :当前线程通知全部等待在该对象上的线程,将全部在等待队列中的线程所有移到同步队列中。

假设A和B须要获取同一把锁,A进入以后,B进入同步队列,陷入阻塞(BLOCKED)。

若是A中调用锁的wait()方法,A释放锁,并陷入等待(WAITING)。此时另一个线程B获取的当前锁,B运行。

若是此时B中调用锁的notify()方法,A被唤醒,从等待队列转移到同步队列,只有B运行完毕了,锁被释放了,A拿到锁了,A才出来运行。

等待/通知机制依托于同步机制,确保等待线程从wait()方法返回时可以感知到通知线程对变量作出的修改

面试常问的几个问题

sleep方法和wait方法的区别

sleep()和wait()方法均可以让线程放弃CPU一段时间,进入等待(WAITING)状态

sleep()静态方法定义在Thread类中,wait()定义在Object类中。

若是线程持有某个对象的监视器,wait()调用以后,当前线程会释放锁,而sleep()则不会释放这个锁

关于放弃对象监视器

对于放弃对象监视器,wait()方法和notify()/notifyAll()有必定区别:

锁对象调用wait()方法以后,会当即释放对象监视器。而notify()/notifyAll()则不会当即释放,而是等到线程剩余代码执行完毕以后才会释放监视器。


参考书籍:《Java并发编程的艺术》 方腾飞

相关文章
相关标签/搜索