//条件队列属性: 只有两个属性 firstWaiter 和 lastWaiter;
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
public ConditionObject() { }
}
条件队列和堵塞队列的关系:
- 每个 condition 有一个关联的条件队列,条件队列是一个单向链表;
- 调用
condition1.signal()
触发一次唤醒,此时唤醒的是队头,会将 firstWaiter(队头) 移到阻塞队列的队尾,等待获取锁,获取锁后 await 方法才能返回
private static final Lock lock = new ReentrantLock();
private static final Condition fullCondition = lock.newCondition(); //队列满的条件
private static final Condition emptyCondition = lock.newCondition(); //队列空的条件
/**
* 生产者
*/
public static class Producer extends Thread{
@Override
public void run(){
while(true){
//获得锁
lock.lock();
while(queue.size() == maxSize){
try {
//条件不满足,生产阻塞
fullCondition.await();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
queue.offer(i++);
//唤醒其他所有生产者、消费者
fullCondition.signalAll();
emptyCondition.signalAll();
//释放锁
lock.unlock();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 消费者
*/
public static class Consumer extends Thread{
@Override
public void run(){
while(true){
//获得锁
lock.lock();
while(queue.isEmpty()){
try {
//条件不满足,消费阻塞
emptyCondition.await();
} catch (Exception ex) {
ex.printStackTrace();
}
}
int x = queue.poll();
//唤醒其他所有生产者、消费者
fullCondition.signalAll();
emptyCondition.signalAll();
//释放锁
lock.unlock();
try {
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
按照流程分析:
await:
//直到被通知或者中断否则一直阻塞
public void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//将该线程添加到CONDITION队列中
Node node = addConditionWaiter();
//该节点加入condition队列中等待,await则需要释放掉当前线程占有的锁
int savedState = fullyRelease(node);
int interruptMode = 0;
//出了while循环,代表线程中断/已经将该node从CONDITION队列transfer到了CLH队列中
//判断该节点是否在CLH队列中
while (!isOnSyncQueue(node)) {
//不在,则阻塞该节点
// 有以下情况会让 LockSupport.park(this); 这句返回继续往下执行:
//
//常规路径。signal -> 转移节点到阻塞队列 -> 获取了锁(unpark)
//线程中断。在 park 的时候,另外一个线程对这个线程进行了中断
//signal 的时候我们说过,转移以后的前驱节点取消了,或者对前驱节点的CAS操作失败了
//假唤醒。这个也是存在的,和 Object.wait() 类似,都有这个问题
LockSupport.park(this);
//在阻塞的过程中发生中断
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//acquireQueued在队列中获取锁,会阻塞当前线程,
//并且在上面while循环等待的过程中没有发生异常,则修改interruptMode状态为REINTERRUPT
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//该节点在阻塞的过程中发生中断,会加入阻塞队列,
//此时该节点的nextWaiter不为null,需要调用unlinkCancelledWaiters将该节点从CONDITION队列中删除,该节点的状态为0
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//如果interruptMode不为0,则代表该线程在上面过程中发生了中断或者抛出了异常,则调用reportInterruptAfterWait方法在此处抛出异常
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
1.如果线程中断,抛出异常InterruptedException;
2.将该线程添加到CONDITION队列的队尾
//首先检查尾节点是否为cancelled状态的节点,如果是则调用unlinkCancelledWaiters删除CONDITION队列中所有cancelled状态的节点,不是,则直接将该新创建的节点添加到CONDITION队列的末尾。
private Node addConditionWaiter() {
//尾指针
Node t = lastWaiter;
//如果尾节点状态是cancelled,则调用unlinkCancelledWaiters方法删除CONDITION链表中所有cancelled状态的节点
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
//t为新的尾节点
t = lastWaiter;
}
//创建一个node节点,状态为CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//添加到队尾
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
//遍历一次CONDITION链表,删除状态为CANCELLED的节点。
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
//下一个节点
Node next = t.nextWaiter;
//如果t的状态是cancelled的,则需要删除t
if (t.waitStatus != Node.CONDITION) {
//清除t的nextWaiter连接
t.nextWaiter = null;
//删除的是首节点
if (trail == null)
firstWaiter = next;
else
//直接将前一个节点的连接指向该节点的下一个节点
trail.nextWaiter = next;
//设置新的尾节点
if (next == null)
lastWaiter = trail;
}
//状态为CONDITION的节点不需要清除
else
trail = t;
t = next;
}
}
3.该节点加入condition队列中等待,await则需要释放掉当前线程占有的锁
//完全释放锁,释放成功则返回,失败则将当前节点的状态设置成cancelled表示当前节点失效
final int fullyRelease(Node node) {
boolean failed = true;
try {
//获取当前锁重入的次数
int savedState = getState();
//释放锁
if (release(savedState)) {
//释放成功
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
//释放锁失败,则当前节点的状态变为cancelled(此时该节点在CONDITION队列中)
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
//尝试释放锁,释放成功则调用unparkSuccessor唤醒后继节点
public final boolean release(int arg) {
//调用tryRelease释放锁。
if (tryRelease(arg)) {
//释放成功,则查看head节点状态,如果不为null且状态不为0(为0表示没有后继或者当前节点已经unparkSuccessor过),则调用unparkSuccessor唤醒后继节点
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//完全释放
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
4.线程中断/已经将该node从CONDITION队列transfer到了CLH队列中,才出while循环(中断:本来有个线程,它是排在条件队列的后面的,但是因为它被中断了,那么它会被唤醒,然后它发现自己不是被 signal 的那个,但是它会自己主动去进入到阻塞队列)
//判断该节点是否在CLH队列中
final boolean isOnSyncQueue(Node node) {
//如果该节点的状态为CONDITION(该状态只能在CONDITION队列中出现,CLH队列中不会出现CONDITION状态),或者该节点的prev指针为null,则该节点一定不在CLH队列中
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
//如果该节点的next(不是nextWaiter,next指针在CLH队列中指向下一个节点)状态不为null,则该节点一定在CLH队列中
if (node.next != null) // If has successor, it must be on queue
return true;
//否则只能遍历CLH队列(从尾节点开始遍历)查找该节点
return findNodeFromTail(node);
}
//从尾节点开始,使用prev指针,遍历整个CLH队列
private boolean findNodeFromTail(Node node) {
Node t = tail;
//从尾节点开始,使用prev指针,开始遍历整个CLH队列
for (;;) {
//找到该节点
if (t == node)
return true;
//遍历完成,没有找到该节点
if (t == null)
return false;
t = t.prev;
}
}
检查线程中断标志:
0:没有发生中断
THROW_IE=-1:通知之前发生中断
REINTERRUPT=1:异常发生在通知中断之后返回重新中断的标志
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
//是否在 await() 期间发生了中断,因为之前的状态等于0,
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//加入阻塞队列
enq(node);
return true;
}
}
5.获取独占锁
此时节点已经进入了阻塞队列,准备获取锁。
//acquireQueued在队列中尝试去获取锁,返回值就是代表线程是否被中断。如果返回 true,说明被中断了,而且 interruptMode != THROW_IE,说明在 signal 之前就发生中断了
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//该节点调用transferAfterCancelledWait添加到CLH队列中的,此时该节点的nextWaiter不为null,需要调用unlinkCancelledWaiters将该节点从CONDITION队列中删除,该节点的状态为0
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//如果interruptMode不为0,则代表该线程在上面过程中发生了中断或者抛出了异常,则调用reportInterruptAfterWait方法在此处抛出异常
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);