线程间通信的模型有两种:共享内存 和 消息传递,以下方式都是基本这两种模型来实现的。
我们来基本一道面试常见的题目来分析:
对一个值两个线程对其交替 +1 -1 (线程间通信)

每个线程主要做五件事:
上锁
判断(等待)
干活
通知
解锁

3.1 synchronized 方案

  1. package juc.wait3;
  2. import java.util.concurrent.Callable;
  3. /**
  4. * @Author Rock Wang
  5. * @Time 2021/11/3
  6. */
  7. public class Main {
  8. public static void main(String[] args) {
  9. Share share = new Share();
  10. new Thread(() -> {
  11. for (int i = 0; i < 30; i++) {
  12. try {
  13. share.incr();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }, "AA").start();
  19. new Thread(() -> {
  20. for (int i = 0; i < 30; i++) {
  21. try {
  22. share.decr();
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }
  27. }, "BB").start();
  28. }
  29. }
  30. /**
  31. * Share 共享
  32. */
  33. class Share {
  34. private int number = 0;
  35. /**
  36. * +1
  37. */
  38. public synchronized void incr() throws InterruptedException {
  39. //判断 干活 通知
  40. //if (number != 0) {
  41. while (number != 0) {
  42. this.wait();
  43. }
  44. number++;
  45. System.out.println(Thread.currentThread().getName() + ",number :" + number);
  46. //唤醒其他所有线程
  47. this.notifyAll();
  48. }
  49. /**
  50. * -1
  51. */
  52. public synchronized void decr() throws InterruptedException {
  53. //if (number != 1) {
  54. while (number != 1) {
  55. this.wait();
  56. }
  57. number--;
  58. System.out.println(Thread.currentThread().getName() + ",number :" + number);
  59. //唤醒其他所有线程
  60. this.notifyAll();
  61. }
  62. }

如果线程超过两个,if判断的过程会出现虚假唤醒问题,导致无法交替加减。
wait() 在哪里睡,在哪里被唤醒,导致if无效,更换while循环即可。

四个线程
AA ++
BB —
CC ++
DD —

都是调用start方法创建,但是先后顺序不一定,睡眠唤醒也不一定。

例子:坐飞机,下飞机再登机需要重新安检(while)。

3.2 Lock 方案

Condition 状况,状态;条件,环境

  1. package juc.lock2;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. /**
  6. * @Author Rock Wang
  7. * @Time 2021/11/3
  8. */
  9. public class Await4 {
  10. public static void main(String[] args) {
  11. Share share = new Share();
  12. for (int i = 0; i < 20; i++) {
  13. new Thread(() -> {
  14. try {
  15. share.incr();
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }, "AA").start();
  20. new Thread(() -> {
  21. try {
  22. share.decr();
  23. } catch (InterruptedException e) {
  24. e.printStackTrace();
  25. }
  26. }, "BB").start();
  27. new Thread(() -> {
  28. try {
  29. share.incr();
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. }, "CC").start();
  34. new Thread(() -> {
  35. try {
  36. share.decr();
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. }, "DD").start();
  41. }
  42. }
  43. }
  44. /**
  45. * Condition 状况,状态;条件,环境
  46. */
  47. class Share {
  48. private int number = 0;
  49. private Lock lock = new ReentrantLock();
  50. private Condition condition = lock.newCondition();
  51. /**
  52. * ++
  53. */
  54. public void incr() throws InterruptedException {
  55. //上锁
  56. lock.lock();
  57. try {
  58. //判断 干活 通知
  59. while (number != 0) {
  60. //等待
  61. condition.await();
  62. }
  63. number++;
  64. System.out.println(Thread.currentThread().getName() + ",number :" + number);
  65. condition.signalAll();
  66. } finally {
  67. //解锁
  68. lock.unlock();
  69. }
  70. }
  71. /**
  72. * --
  73. */
  74. public void decr() throws InterruptedException {
  75. lock.lock();
  76. try {
  77. while (number != 1) {
  78. condition.await();
  79. }
  80. number--;
  81. System.out.println(Thread.currentThread().getName() + ",number :" + number);
  82. condition.signalAll();
  83. } finally {
  84. lock.unlock();
  85. }
  86. }
  87. }

Condition
await();
signalAll();

3.3线程间定制化通信

==问题: A线程打印5次A,B线程打印10次B,C线程打印15次C,按照此顺序循环10轮==
关键点:需要让线程按照顺序执行。
image.png
1.分别加标志位 flag = 1 2 3
2.判断标志位 1,打印
3.修改标志位 2,通知BB
4.判断标志位 2,打印
5.修改标志位 3,通知CC
6.判断标志位 3,打印
7.修改标志位 1,通知AA
…循环10次

  1. package juc.lock2;
  2. import java.util.concurrent.locks.Condition;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. /**
  6. * @Author Rock Wang
  7. * @Time 2021/11/3
  8. */
  9. public class ThreadDemo3 {
  10. public static void main(String[] args) {
  11. ShareResult shareResult = new ShareResult();
  12. new Thread(() -> {
  13. for (int i = 1; i <= 10; i++) {
  14. try {
  15. shareResult.print5(i);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }, "AA").start();
  21. new Thread(() -> {
  22. for (int i = 1; i <= 10; i++) {
  23. try {
  24. shareResult.print10(i);
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }, "BB").start();
  30. new Thread(() -> {
  31. for (int i = 1; i <= 10; i++) {
  32. try {
  33. shareResult.print15(i);
  34. } catch (InterruptedException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }, "CC").start();
  39. }
  40. }
  41. /**
  42. * 创建资源类
  43. */
  44. class ShareResult {
  45. /**
  46. * 1:AA
  47. * 2:BB
  48. * 3:CC
  49. */
  50. private int flag = 1;
  51. /**
  52. * 创建Lock锁
  53. */
  54. private Lock lock = new ReentrantLock();
  55. /**
  56. * 创建三个Condition
  57. */
  58. private Condition c1 = lock.newCondition();
  59. private Condition c2 = lock.newCondition();
  60. private Condition c3 = lock.newCondition();
  61. /**
  62. * 打印五次
  63. *
  64. * @param loop 第几轮
  65. */
  66. public void print5(int loop) throws InterruptedException {
  67. //上锁
  68. lock.lock();
  69. try {
  70. //判断
  71. while (flag != 1) {
  72. c1.await();
  73. }
  74. for (int i = 0; i < 5; i++) {
  75. // 干活
  76. System.out.println(Thread.currentThread().getName() + " :: " + (i + 1) + ",轮数: " + loop);
  77. }
  78. //修改标志位
  79. flag = 2;
  80. //通知
  81. c2.signalAll();
  82. } finally {
  83. lock.unlock();
  84. }
  85. }
  86. /**
  87. * 打印10次
  88. *
  89. * @param loop 第几轮
  90. */
  91. public void print10(int loop) throws InterruptedException {
  92. //上锁
  93. lock.lock();
  94. try {
  95. //判断
  96. while (flag != 2) {
  97. c2.await();
  98. }
  99. for (int i = 0; i < 10; i++) {
  100. // 干活
  101. System.out.println(Thread.currentThread().getName() + " :: " + (i + 1) + ",轮数: " + loop);
  102. }
  103. //修改标志位
  104. flag = 3;
  105. //通知
  106. c3.signalAll();
  107. } finally {
  108. lock.unlock();
  109. }
  110. }
  111. /**
  112. * 打印15次
  113. *
  114. * @param loop 第几轮
  115. */
  116. public void print15(int loop) throws InterruptedException {
  117. //上锁
  118. lock.lock();
  119. try {
  120. //判断
  121. while (flag != 3) {
  122. c3.await();
  123. }
  124. //干活
  125. for (int i = 0; i < 15; i++) {
  126. System.out.println(Thread.currentThread().getName() + " :: " + (i + 1) + ",轮数: " + loop);
  127. }
  128. //修改标志位
  129. flag = 1;
  130. //通知
  131. c1.signalAll();
  132. } finally {
  133. lock.unlock();
  134. }
  135. }
  136. }