synchronized中也有条件变量,就是Monitor对象中的WaitSet,当线程条件不满足/调用wait方法后会进入WaitSet中等待
ReentrantLock的条件变量比synchronized强大之处在于,它支持多个条件变量,这就好比:
- synchronized是那些不满足条件的线程都在一间休息室等消息
- ReentrantLock支持多件休息室,有专门等烟的休息室,专门等早餐的休息室,唤醒时也是按休息室来唤醒
使用流程(与synchronized的wait-notify方法对比记忆)
- await前需要获得锁(与wait方法类似)
- await执行后,会释放锁,进入conditionObject等待
- await的线程被唤醒、打断、超时后会重新竞争lock锁(与synchronized类似,例如从WaitSet中进入EntryList中竞争锁)
- 竞争lock锁成功后,从await处的代码继续向后执行。(这里也复习一下wait方法,当被唤醒、打断、超时后进入EntryList,只有重新竞争到锁才会继续从wait代码处向下执行完毕,可以理解为wait-notify本身就是在synchronized块内的,如果再次执行也势必要重新获得锁)
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestCondition {
static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
//创建一个新的条件变量(休息室)
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
lock.lock();//主线程先获得锁
//进入休息室中
condition1.await();//主线程在condition1中等待
}
}
创建条件变量:
Condition condition1 = lock.newCondition();
线程在条件变量上等待:
condition1.await()
唤醒线程:
//随机唤醒在condition1对象上等待的一个线程
condition1.signal();
//唤醒在condition1对象上等待的所有线程
condition1.signalAll();
送烟送外卖例子改为如下代码:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestCondition {
static ReentrantLock ROOM = new ReentrantLock();//创建锁对象
static Condition waitCigaretteSet = ROOM.newCondition();//等待烟的队列
static Condition waitTakeoutSet = ROOM.newCondition();//等待外卖的队列
static boolean hasCigarette = false;//有没有烟
static boolean hasTakeout = false;//有没有外卖
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
ROOM.lock();
try{
System.out.println("有烟没?"+hasCigarette);
while(!hasCigarette){
System.out.println("没烟,先歇会儿");
try {
waitCigaretteSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("有烟没?"+hasCigarette);
if(hasCigarette){
System.out.println("可以开始干活了");
}else{
System.out.println("没干成活");
}
}finally {
ROOM.unlock();
}
},"小南").start();
new Thread(()->{
ROOM.lock();
try{
System.out.println("外卖送到没?"+hasTakeout);
while(!hasTakeout){
System.out.println("外卖没到呢,等着吧您");
try {
waitTakeoutSet.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("外卖到没"+hasTakeout);
if(hasCigarette){
System.out.println("吃上外卖了");
}else{
System.out.println("饿死我了,还没吃上外卖");
}
}finally {
ROOM.unlock();
}
},"小女").start();
Thread.sleep(1000);//睡眠1秒
//送外卖线程
new Thread(()->{
ROOM.lock();//还是先获得锁,signal方法也要在获得锁的情况下使用
try{
hasTakeout=true;
waitTakeoutSet.signal();//唤醒等待线程
System.out.println("外卖送到了嗷!");
}finally {
ROOM.unlock();
}
},"送外卖的").start();
Thread.sleep(1000);//睡眠1秒
//送烟线程
new Thread(()->{
ROOM.lock();//还是先获得锁,signal方法也要在获得锁的情况下使用
try{
hasCigarette=true;
waitCigaretteSet.signal();//唤醒等待线程
System.out.println("烟送到了嗷!");
}finally {
ROOM.unlock();
}
},"送烟的").start();
}
}
实际上与synchronized的wait-notify写法的逻辑一模一样,只是语法不同。