Condition由来
在前面synchronized的时候,有讲到wait/notify的基本使用,结合shnchronized
可以实现对线程通信。那么这个时候我就思考了,既然JUC里面提供了锁的实现机制,那么
JUC里面有没有提供类似的线程通信的工具呢?于是找啊找,发现了一个condition工具类
Condition是一个多线程协调通信工具类,可以让某些线程一起等待某个条件(con
dition),只有满足条件时,线程才会被唤醒, 案例如下:
public class ConditionDemoWait implements Runnable {
private Lock lock;
private Condition condition;
public ConditionDemoWait(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin---ConditionDemoWait");
try {
lock.lock();
condition.await();
System.out.println("end---ConditionDemoWait");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ConditionDemoSignal implements Runnable {
private Lock lock;
private Condition condition;
public ConditionDemoSignal(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("begin---ConditionDemoSignal");
try {
lock.lock();
condition.signal();
System.out.println("end---ConditionDemoSignal");
} finally {
lock.unlock();
}
}
}
public class MainClient {
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread t1 = new Thread(new ConditionDemoWait(lock,condition));
Thread t2 = new Thread(new ConditionDemoSignal(lock,condition));
t1.start();
t2.start();
}
}
运行结果如下:
begin---ConditionDemoWait
begin---ConditionDemoSignal
end---ConditionDemoSignal
end---ConditionDemoWait
官方解释:
通过这个案例实现了wait() 和notify()的功能,当调用await方法后,当前线程会释放并等待,而其他线程调用condition对象的signal或者signalAll方法通知被阻塞的线程,然后自己执行unlock释放锁,被唤醒的线程获得之前的锁继续执行,最后释放锁。
所以,condition中有两个最重要的方法
1、await 把当前线程阻塞挂起
2、signal 唤醒等待队列中的线程
其实这样的结果不难理解:
1、首选执行的是ConditionDemoWait这个类,先打印begin---ConditionDemoWait,之后ConditionDemoWait连执行了condition.await()方法
这个condition.await() 这个方法执行的是释放锁并进入condition的等待队列,类似于wait() 方法
2、当ConditionDemoWait方法释放锁之后,那么相对阻塞的线程ConditionDemoSignal得以获得锁,所以打印begin---ConditionDemoSignal,之后
ConditionDemoSignal执行condition.signal()方法,这个方法是干嘛的呢?它是唤醒被阻塞的线程(被唤醒的线程可以去竞争锁,但是现在锁没有被释放)
所以紧接着打印:end---ConditionDemoSignal,当ConditionDemoSignal执行lock.unlock()的时候,表示当前线程执行完毕,锁被释放
3、ConditionDemoWait竞争锁并获取,因此打印end---ConditionDemoWait
Condition 源码分析
调用Condition需要获得Lock锁,所以意味着会存在一个AQS同步队列,在上面那个案例中,假如两个线程同时运行的话,那么AQS的队列可能是下面这种情况
前序:
Thread 用来存放进入AQS队列里面的线程
SHARED 用来标记该线程是获取共享资源是阻塞挂起后放入AQS队列
EXCLUSIVE 用来标记线程是获取独占资源时被挂起后放入AQS队列
waitStatus 记录当前线程等待状态
* CANCELLED 线程取消了
* SIGNAL 线程需要被唤醒
* CONDITION 线程在条件队列里等待
* PROPAGATE 释放共享资源时需要通知其他结点
pre 记录当前结点的前驱结点
next 记录当前结点的后继结点
解释一下:
当俩线程访问的时候,当线程A获取到锁的时候,那么会修改共享变量state的值,并且将execlusiveOwnerThread的线程改为ThreadA,下面的队列为阻塞队列,等待共享变量state的值为0,接着往下看
那么这个时候ThreadA调用了condition.await()方法,它做了什么事情呢?
condition.await()
调用condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁
下面我们来看看await相关的代码:
/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
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;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
我们沿用老方法先解读注释:
1、可中断的条件等待
2、如果当前线程被中断则抛出异常
3、通过getStatus()获取当前state的值
4、将state作为一个参数当我们去调用release的时候
5、当前线程被阻塞直到同一个lock下执行signal()或者当前线程被中断
6、调用acquire以state作为参数
7、如果在步骤4被中断则抛出异常
下面我们来仔细看下代码的实现:
if (Thread.interrupted())
throw new InterruptedException();
这一句代码不就是说的,如果当前线程被中断则抛出异常,说的是第一步
Node node = addConditionWaiter();
这句代码也很有意思,回忆一下是不是跟Reentrant里面的addWaiter()有点像呢?
解释一下:
当俩线程访问的时候,当线程A获取到锁的时候,那么会修改共享变量state的值,并且将execlusiveOwnerThread的线程改为ThreadA,下面的队列为阻塞队列,等待共享变量state的值为0,接着往下看
那么这个时候ThreadA调用了condition.await()方法,它做了什么事情呢?
condition.await()
调用condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁
下面我们来看看await相关的代码:
/**
* Implements interruptible condition wait.
* <ol>
* <li> If current thread is interrupted, throw InterruptedException.
* <li> Save lock state returned by {@link #getState}.
* <li> Invoke {@link #release} with saved state as argument,
* throwing IllegalMonitorStateException if it fails.
* <li> Block until signalled or interrupted.
* <li> Reacquire by invoking specialized version of
* {@link #acquire} with saved state as argument.
* <li> If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
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;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
我们沿用老方法先解读注释:
1、可中断的条件等待
2、如果当前线程被中断则抛出异常
3、通过getStatus()获取当前state的值
4、将state作为一个参数当我们去调用release的时候
5、当前线程被阻塞直到同一个lock下执行signal()或者当前线程被中断
6、调用acquire以state作为参数
7、如果在步骤4被中断则抛出异常
下面我们来仔细看下代码的实现:
if (Thread.interrupted())
throw new InterruptedException();
这一句代码不就是说的,如果当前线程被中断则抛出异常,说的是第一步
Node node = addConditionWaiter();
这句代码也很有意思,回忆一下是不是跟Reentrant里面的addWaiter()有点像呢?
场景1: AB两个线程去争抢锁,线程A抢到了锁,之后线程A执行await()如下图所示:
场景2: 此时B线程开始执行,线程A处于阻塞状态,此时A阻塞在await()方法
场景3: 此时B线程执行await(),线程A处于阻塞状态,此时A阻塞在await()方法
场景4: ThreadB正在运行await(),ThreadA被唤醒
以上就是针对Condition–await()/signal()的解析,谢谢大家,有问题可以指点出来,谢谢大佬!
印象笔记地址: https://app.yinxiang.com/fx/d3c47cab-acc6-41ad-b143-72960cbb082d