Java并发包:AQS的Condition类
文章目录
Condition介绍
Condition介绍
Condition(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wai
t 做的那样
在Condition中,用await(
)替换wait()
,用signal()
替换notify()
,用signalAll()
替换notifyAll()
,传统线程的通信方式,Condition都可以实现。
值得注意的是当condition.await()
时,隐式的将条件变量关联的Lock解锁,而使其他线程有机会获得Lock,而检查条件,并在条件满足时,等待在条件变量上。
常用方法
Condition中主要的方法有2个
- await()方法可以阻塞当前线程,并释放锁。
- 在获取锁后可以调用signal()通知被await()阻塞的线程"激活"。
这里的await(),signal()必须在ReentrantLock#lock()和ReentrantLock#unlock()之间调用。
代码分析
ConditionObject类
通过 ReentrantLock.newCondition();
查看Condition
的实现类
/**
* 这里要区分condition queue 和 sync queue 两个概念
*/
public class ConditionObject implements Condition, java.io.Serializable{
private static final long serialVersionUID = 1173984872572414699L;
//condition队列的 首节点
private transient Node firstWaiter;
//condition队列的 尾结点
private transient Node lastWaiter;
public ConditionObject() { }
//将当前线程加入到等待队列中
private Node addConditionWaiter() {
//省略实现代码...
}
//将condition queue的节点加入到sync queue中
private void doSignal(Node first) {
//将node的状态从CONDITION改成0
//从condition queue 去掉node
//在sync queue 加上node
//将node的状态从0改成SIGNAL
//唤醒线程
}
//操作所有节点 doSignal
private void doSignalAll(Node first) {
//省略实现代码...
}
//firstWaiter 调用 doSignal
public final void signal() {
//省略实现代码...
}
//firstWaiter 调用 doSignalAll
public final void signalAll() {
//省略实现代码...
}
//省略中间代码...
//将当前线程加入condition queue
public final void await() throws InterruptedException {
//加入condition queue
//释放资源
//阻塞线程直到被加入到sync queue
}
//当前线程等待nanos时间
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
//省略实现代码...
}
//当前线程等待至一个具体时间
public final boolean awaitUntil(Date deadline)
throws InterruptedException {
//省略实现代码...
}
//同理 ,等待固定时间,有单位设置
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
//省略实现代码...
}
//省略其他代码...
}
主要看两个方法:
- doSignal()
- await()
doSignal()方法
public final void signal() {
//判断是否独占锁,没加锁会报错
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
//取出condition头结点
if ( (firstWaiter = first.nextWaiter) == null)
//如果取出之后新的头结点为空的话,做null处理操作
lastWaiter = null;
first.nextWaiter = null;
//transferForSignal 操作线程节点从condition queue到sync queue方法
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
/*
* CAS操作节点状态,从condition到0,如果操作失败,则表明节点被取消
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* enq方法将节点放入在sync queue里面,返回node节点前一个节点
*/
Node p = enq(node);
int ws = p.waitStatus;
//如果前一个节点被取消,或者修改前一个节点状态失败,则马上唤醒当前节点
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
//这个方法主要就是把节点加入到同步阻塞队列的,真正的激活则是调用unlock()去处理。
return true;
}
await()方法
public final void await() throws InterruptedException {
//如果线程中断,抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
//当前节点加入condition queue
Node node = addConditionWaiter();
//释放锁资源
int savedState = fullyRelease(node);
int interruptMode = 0;
// 阻塞直到节点被加入sync queue
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
//检查中断的原因
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//清楚后面节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//中断抛出异常
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
private Node addConditionWaiter() {
Node t = lastWaiter;
// 尾结点被取消,清理
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//创建节点 状态为condition
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
final int fullyRelease(Node node) {
boolean failed = true;
try {
//调用getState()先获取阻塞队列中当前线程节点的锁状态值,这个值可能大于1表示多次重入
int savedState = getState();
//然后调用release(savedState)释放所有锁,如果释放成功返回锁状态值。
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
final boolean isOnSyncQueue(Node node) {
//判断当前状态是否condition ,有没有存在于sync queue
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
//遍历查询sync queue
return findNodeFromTail(node);
}
示例代码
main方法
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(()->{
lock.lock();
System.out.println(Thread.currentThread().getId()+" - 启动...");
try {
System.out.println(Thread.currentThread().getId()+" - 睡眠...");
condition.await();
System.out.println(Thread.currentThread().getId()+" - 被唤醒...");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
});
executorService.submit(()->{
lock.lock();
System.out.println(Thread.currentThread().getId()+" - 唤醒线程 启动...");
try {
Thread.sleep(5000);
System.out.println(Thread.currentThread().getId()+" - 唤醒线程 唤醒...");
condition.signal();
System.out.println(Thread.currentThread().getId()+" - 唤醒线程 唤醒完毕...");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//如果此处注释,则不会真正的唤醒
lock.unlock();
}
});
}