简介
Java 中任何对象都可以被synchronized加锁实现线程同步,其原因是Java中任何类都继承了Object类,Object类中实现了线程通讯的方法如:wait(),wait(long timeout),wait(long timeout, int nanos)与notify(),notifyAll(),通过这些方法可以实现线程的阻塞与唤醒,因为AQS队列是Java在代码层面实现的锁因此也提供了线程间通讯的方法,本文将分析AQS队列中ConditionObject内部类以及探索如何实现线程间的通讯
Condition原理分析
在AQS队列中ConditionObject实现了Condition接口该内部类才是具体实现,condition的使用主要通过如下方式创建
Condition con = lock.newCondition();
wait和signal调用方式分别为
con.await();
con.signal();
在ConditionObject内部复用了AQS的Node作为等待队列的基本元素
lock可以创建多个等待队列,这一点与Object只能创建一个等待队列不同
ConditionObject内部成员变量很简单,一个头指针指,一个为指针分别指向登台队列
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
本文重点分析await和signal方法
- await 方法首先会检查当前线程是否被中断过,如果是直接抛出中断异常
- 向等待队列尾部添加节点
- 释放当前线程占用的锁同时唤醒同步队列中的节点开始竞争锁
- 当signal方法被调用后或者产生中断当前线程才会退出await方法
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//线程在被signal后需要重新获取到锁并执行完await方法后退出
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
addConditionWaiter方法分析
- 首先判断队列尾部节点状态如果状态是非condition状态需要清理
- 将当前线程放入node节点插入队列尾部
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
isOnSyncQueue判断当前节点是否在同步队列中
- 首先判断当前节点的状态如果是CONDITION或者prev为null则该节点一定不在同步队列中,如果next不为空则一定在同步队列中
- 从同步队列尾部开始判断节点是都在队列中
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
return findNodeFromTail(node);
}
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
signal方法分析
调用condition的signal或者signalAll方法可以将等待队列中等待时间最长的节点移动到同步队列中,使得该节点能够有机会获得lock。按照等待队列是先进先出(FIFO)的,所以等待队列的头节点必然会是等待时间最长的节点,也就是每次调用condition的signal方法是将头节点移动到同步队列中,代码通过doSignal方法将等待队列中的第一个节点进行释放
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
//1.将头结点的状态更改为CONDITION;
//2.调用enq方法,将该节点尾插入到同步队列
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
- 参考文章 https://www.jianshu.com/p/28387056eeb4