在java中,线程间的同步主要通过两个方面来实现:
锁 —— 用于控制线程级的同步
条件 —— 用于实现线程间的通信和协作
围绕这套线程同步机制,有三个组件:
一个互斥的锁对象(多个线程只有获取到该锁对象才能进入到同步代码块,可以是Object对象监视器锁、或者Lock对象)
一个锁等待队列(waiting_queue,用于容纳等待上面锁对象的线程们)
一个或者多个条件等待队列(condition_queue,某个线程在获得锁后,执行条件等待时进入该队列,在条件满足后从该队列移除,并进入到上面的锁等待队列,等待下一次获取锁的机会)
jdk中的具体实现有2套:
Object对象监视器锁、Object#wait()、notify()和notifyAll();
Lock锁、条件变量Condition#await()、signal()和signalAll();
他们之间的区别:
Object的机制在实际中更常用,而Lock的机制更灵活;
Object和Lock的条件等待不太一样:
Object#wait()仅对应一个等待线程集,也即一个监视器锁仅支持一个等待条件;
Lock和Condition可以支持一个锁对象对应多个等待条件,可以有多个条件等待队列。
我手画了一张Object和Lock同步机制的原理实现:
AQS中的锁等待队列和条件队列的实现都是一个由内部类Node节点(代表具体某个线程)组成的双向链表来实现:
* +------+ prev +-----+ +-----+
* head | |
* +------+ +-----+ +-----+
条件队列 Condition Queue
Java中的条件队列可以有效地实现和管理状态依赖性的操作,其有两种实现方式:
内置的条件队列Object#wait()、notify()和notifyAll()
显示的Condition#await()、signal()和signalAll(),但它比前者扩展了功能,如:可实现一个Lock对象对应多个条件队列、可中断、可定时、公平与非公平等待队列唤醒机制。
引用JDK Condition的javadoc:
内置条件队列与内置锁紧密绑定在一起,显示Condition与显示Lock紧密绑定在一起。这是因为管理状态依赖性的机制必须和确保状态一致性的机制关联在一起。
Object#wait()的使用范例:
Lock和Condition的使用范例: