线程间通信的模型有两种:共享内存和消息传递
线程间的通信具体步骤:(涉及上中下部)

  1. 创建资源类,在资源类中船舰属性和操作方法
  2. 在资源类操作方法:判断、操作、通知
  3. 创建多个线程,调用资源类的操作方法
  4. 防止虚拟唤醒问题

    2.1 synchronized案例

    操作线程的时候,等待线程使用wait()
    通知另外的线程操作用notify()、notifyAll()
    假设有两个线程,该线程在执行过程中,判断值(不是该值等待,让其他线程抢),操作值,通知另外一个线程的调度

通过使用两个线程对0这个值操作,一个线程加1,一个线程减1,交替实现多次

  1. //第一步 创建资源类,定义属性和操作方法
  2. class Share {
  3. //初始值
  4. private int number = 0;
  5. //+1的方法
  6. public synchronized void incr() throws InterruptedException {
  7. //第二步 判断 干活 通知
  8. if(number != 0) { //判断number值是否是0,如果不是0,等待
  9. this.wait(); //在哪里睡,就在哪里醒
  10. }
  11. //如果number值是0,就+1操作
  12. number++;
  13. System.out.println(Thread.currentThread().getName()+" :: "+number);
  14. //通知其他线程
  15. this.notifyAll();
  16. }
  17. //-1的方法
  18. public synchronized void decr() throws InterruptedException {
  19. //判断
  20. if(number != 1) {
  21. this.wait();
  22. }
  23. //干活
  24. number--;
  25. System.out.println(Thread.currentThread().getName()+" :: "+number);
  26. //通知其他线程
  27. this.notifyAll();
  28. }
  29. }
  30. public class ThreadDemo1 {
  31. //第三步 创建多个线程,调用资源类的操作方法
  32. public static void main(String[] args) {
  33. Share share = new Share();
  34. //创建线程
  35. new Thread(()->{
  36. for (int i = 1; i <=10; i++) {
  37. try {
  38. share.incr(); //+1
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. },"AA").start();
  44. new Thread(()->{
  45. for (int i = 1; i <=10; i++) {
  46. try {
  47. share.decr(); //-1
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. },"BB").start();
  53. }
  54. }

代码截图
image.png
如果使用多个线程,添加额外两个线程,且操作要依次执行

  1. new Thread(()->{
  2. for (int i = 1; i <=10; i++) {
  3. try {
  4. share.incr(); //+1
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. }
  9. },"CC").start();
  10. new Thread(()->{
  11. for (int i = 1; i <=10; i++) {
  12. try {
  13. share.decr(); //-1
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. },"DD").start();

执行结果
image.png
主要是虚拟唤醒导致:如果一个线程执行完毕后,通知其他线程,该线程又进入等待睡眠,可能会因为某些原因被唤醒后,if结构的语句就不会判断了,一直往下执行,所以需要将if换成while结构,每次都判断。因为wait在哪里睡眠就在哪里被唤醒,结果被某个异常唤醒了后回不去了,if结构不会在判断了,需要更改为while

  1. while(number != 0) { //判断number值是否是0,如果不是0,等待
  2. this.wait(); //在哪里睡,就在哪里醒
  3. }

实现中断和虚假唤醒是可能的,需要将其while方法用在循环中
image.png

2.2 Lock案例

使用lock先要创建锁的对象以及通知的对象
放置在资源类中

  1. //创建Lock
  2. private Lock lock = new ReentrantLock();
  3. private Condition condition = lock.newCondition();

上锁lock.lock();
解锁lock.unlock();
以下都为condition类:
唤醒所有等待的线程signalAll(),带上类名condition.signalAll();
唤醒一个等待线程signal(),带上类名,condition.signal();
造成当前线程在接到信号或者被中断之前一直处于等待状态await(),带上类名,condition.await();

同样是上面的案例题目换成lock
比如一个加1操作

  1. //+1
  2. public void incr() throws InterruptedException {
  3. //上锁
  4. lock.lock();
  5. try {
  6. //判断
  7. while (number != 0) {
  8. condition.await();
  9. }
  10. //干活
  11. number++;
  12. System.out.println(Thread.currentThread().getName()+" :: "+number);
  13. //通知
  14. condition.signalAll();
  15. }finally {
  16. //解锁
  17. lock.unlock();
  18. }
  19. }

具体完整的代码示列
一般调用资源类的方法,先调用,在idea中使用快捷键抛出异常

  1. //第一步 创建资源类,定义属性和操作方法
  2. class Share {
  3. private int number = 0;
  4. //创建Lock
  5. private Lock lock = new ReentrantLock();
  6. private Condition condition = lock.newCondition();
  7. //+1
  8. public void incr() throws InterruptedException {
  9. //上锁
  10. lock.lock();
  11. try {
  12. //判断
  13. while (number != 0) {
  14. condition.await();
  15. }
  16. //干活
  17. number++;
  18. System.out.println(Thread.currentThread().getName()+" :: "+number);
  19. //通知
  20. condition.signalAll();
  21. }finally {
  22. //解锁
  23. lock.unlock();
  24. }
  25. }
  26. //-1
  27. public void decr() throws InterruptedException {
  28. lock.lock();
  29. try {
  30. while(number != 1) {
  31. condition.await();
  32. }
  33. number--;
  34. System.out.println(Thread.currentThread().getName()+" :: "+number);
  35. condition.signalAll();
  36. }finally {
  37. lock.unlock();
  38. }
  39. }
  40. }
  41. public class ThreadDemo2 {
  42. public static void main(String[] args) {
  43. Share share = new Share();
  44. new Thread(()->{
  45. for (int i = 1; i <=10; i++) {
  46. try {
  47. share.incr();
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. }
  51. }
  52. },"AA").start();
  53. new Thread(()->{
  54. for (int i = 1; i <=10; i++) {
  55. try {
  56. share.decr();
  57. } catch (InterruptedException e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. },"BB").start();
  62. new Thread(()->{
  63. for (int i = 1; i <=10; i++) {
  64. try {
  65. share.incr();
  66. } catch (InterruptedException e) {
  67. e.printStackTrace();
  68. }
  69. }
  70. },"CC").start();
  71. new Thread(()->{
  72. for (int i = 1; i <=10; i++) {
  73. try {
  74. share.decr();
  75. } catch (InterruptedException e) {
  76. e.printStackTrace();
  77. }
  78. }
  79. },"DD").start();
  80. }
  81. }