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块内的,如果再次执行也势必要重新获得锁)
  1. import java.util.concurrent.locks.Condition;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class TestCondition {
  4. static ReentrantLock lock = new ReentrantLock();
  5. public static void main(String[] args) throws InterruptedException {
  6. //创建一个新的条件变量(休息室)
  7. Condition condition1 = lock.newCondition();
  8. Condition condition2 = lock.newCondition();
  9. lock.lock();//主线程先获得锁
  10. //进入休息室中
  11. condition1.await();//主线程在condition1中等待
  12. }
  13. }

创建条件变量:

  1. Condition condition1 = lock.newCondition();

线程在条件变量上等待:

  1. condition1.await()

唤醒线程:

  1. //随机唤醒在condition1对象上等待的一个线程
  2. condition1.signal();
  3. //唤醒在condition1对象上等待的所有线程
  4. condition1.signalAll();

送烟送外卖例子改为如下代码:

  1. import java.util.concurrent.locks.Condition;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class TestCondition {
  4. static ReentrantLock ROOM = new ReentrantLock();//创建锁对象
  5. static Condition waitCigaretteSet = ROOM.newCondition();//等待烟的队列
  6. static Condition waitTakeoutSet = ROOM.newCondition();//等待外卖的队列
  7. static boolean hasCigarette = false;//有没有烟
  8. static boolean hasTakeout = false;//有没有外卖
  9. public static void main(String[] args) throws InterruptedException {
  10. new Thread(()->{
  11. ROOM.lock();
  12. try{
  13. System.out.println("有烟没?"+hasCigarette);
  14. while(!hasCigarette){
  15. System.out.println("没烟,先歇会儿");
  16. try {
  17. waitCigaretteSet.await();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. System.out.println("有烟没?"+hasCigarette);
  23. if(hasCigarette){
  24. System.out.println("可以开始干活了");
  25. }else{
  26. System.out.println("没干成活");
  27. }
  28. }finally {
  29. ROOM.unlock();
  30. }
  31. },"小南").start();
  32. new Thread(()->{
  33. ROOM.lock();
  34. try{
  35. System.out.println("外卖送到没?"+hasTakeout);
  36. while(!hasTakeout){
  37. System.out.println("外卖没到呢,等着吧您");
  38. try {
  39. waitTakeoutSet.await();
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. System.out.println("外卖到没"+hasTakeout);
  45. if(hasCigarette){
  46. System.out.println("吃上外卖了");
  47. }else{
  48. System.out.println("饿死我了,还没吃上外卖");
  49. }
  50. }finally {
  51. ROOM.unlock();
  52. }
  53. },"小女").start();
  54. Thread.sleep(1000);//睡眠1秒
  55. //送外卖线程
  56. new Thread(()->{
  57. ROOM.lock();//还是先获得锁,signal方法也要在获得锁的情况下使用
  58. try{
  59. hasTakeout=true;
  60. waitTakeoutSet.signal();//唤醒等待线程
  61. System.out.println("外卖送到了嗷!");
  62. }finally {
  63. ROOM.unlock();
  64. }
  65. },"送外卖的").start();
  66. Thread.sleep(1000);//睡眠1秒
  67. //送烟线程
  68. new Thread(()->{
  69. ROOM.lock();//还是先获得锁,signal方法也要在获得锁的情况下使用
  70. try{
  71. hasCigarette=true;
  72. waitCigaretteSet.signal();//唤醒等待线程
  73. System.out.println("烟送到了嗷!");
  74. }finally {
  75. ROOM.unlock();
  76. }
  77. },"送烟的").start();
  78. }
  79. }

实际上与synchronized的wait-notify写法的逻辑一模一样,只是语法不同。