Condition
2019-01-04 本文已影响0人
CoderZzbJohn
package com.meituan.hyt.test4;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Main {
public static void main(String[] args) {
final ReentrantLock reentrantLock = new ReentrantLock();
final Condition condition = reentrantLock.newCondition();
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + "拿到锁了");
System.out.println(Thread.currentThread().getName() + "等待信号");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到信号");
reentrantLock.unlock();
}
}, "线程1").start();
new Thread(new Runnable() {
@Override
public void run() {
reentrantLock.lock();
System.out.println(Thread.currentThread().getName() + "拿到锁了");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "发出信号");
condition.signalAll();
reentrantLock.unlock();
}
}, "线程2").start();
}
}
运行结果:
线程1拿到锁了
线程1等待信号
线程2拿到锁了
线程2发出信号
线程1拿到信号
跟sychronized类似。
reentrantLock.lock() 相当于sychronized(objectA)。
condition.await() 类似于objectA.wait()
condition.signal() 类似于 objectA.notify()
reentrantLock.new多个condition相当于分组管理了。conditionA.await()只能由conditionA.signal()唤醒。conditionB无法唤醒。
原理
我们知道Lock的本质是AQS,AQS自己维护的队列是当前等待资源的队列,AQS会在资源被释放后,依次唤醒队列中从前到后的所有节点,使他们对应的线程恢复执行,直到队列为空。而Condition自己也维护了一个队列,该队列的作用是维护一个等待signal信号的队列。但是,两个队列的作用不同的,事实上,每个线程也仅仅会同时存在以上两个队列中的一个,流程是这样的:
- 线程1调用reentrantLock.lock时,尝试获取锁。如果成功,则返回,从AQS的队列中移除线程;否则阻塞,保持在AQS的等待队列中。
- 线程1调用await方法被调用时,对应操作是被加入到Condition的等待队列中,等待signal信号;同时释放锁。
- 锁被释放后,会唤醒AQS队列中的头结点,所以线程2会获取到锁。
- 线程2调用signal方法,这个时候Condition的等待队列中只有线程1一个节点,于是它被取出来,并被加入到AQS的等待队列中。注意,这个时候,线程1 并没有被唤醒,只是被加入AQS等待队列。
- signal方法执行完毕,线程2调用unLock()方法,释放锁。这个时候因为AQS中只有线程1,于是,线程1被唤醒,线程1恢复执行。
所以:
发送signal信号只是将Condition队列中的线程加到AQS的等待队列中。只有到发送signal信号的线程调用reentrantLock.unlock()释放锁后,这些线程才会被唤醒。
可以看到,整个协作过程是靠结点在AQS的等待队列和Condition的等待队列中来回移动实现的,Condition作为一个条件类,很好的自己维护了一个等待信号的队列,并在适时的时候将结点加入到AQS的等待队列中来实现的唤醒操作。