一、线程间通信
1、概述
- 线程间通信的模型有两种:共享内存和消息传递
2、多线程编程步骤(中)
-
创建资源类,在资源类中创建属性和操作方法
-
在资源类操作方法进行判断、操作、通知
-
创建多个线程,调用资源类中的操作方法
二、synchronized 实现线程间通信
1、需求
- 通过使用两个线程对值(0)进行操作,一个线程加 1,一个线程减 1,交替实现多次
2、具体实现
(1)资源类
- Share 类
package com.my.communicate;public class Share {// 初始值private int number;// 加 1 的方法public synchronized void increase() throws InterruptedException {// number 为 0 则等待if (number != 0) {this.wait();}number++;System.out.println(Thread.currentThread().getName() + " " + number);// 通知其他线程this.notifyAll();}// 减 1 的方法public synchronized void decrease() throws InterruptedException {// number 为 0 则等待if (number != 1) {this.wait();}number--;System.out.println(Thread.currentThread().getName() + " " + number);// 通知其他线程this.notifyAll();}
}
(2)多线程测试
- ShareTest 类
package com.my.communicate;public class ShareTest {public static void main(String[] args) {Share share = new Share();Thread thread1 = new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increase();} catch (InterruptedException e) {e.printStackTrace();}}}, "AA");Thread thread2 = new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrease();} catch (InterruptedException e) {e.printStackTrace();}}}, "BB");thread1.start();thread2.start();}
}
3、虚假唤醒
-
在当前 synchronized 实现线程间通信案例中再增加两个线程,执行结果会不符合预期,根本原因是虚假唤醒问题
-
如果一个线程执行完毕后,通知其他线程,该线程又进入等待睡眠,被唤醒后,if 语句不会进行判断
-
需要将 if 语句换成 while 语句,因为在哪里等待睡眠就会在哪里被唤醒,需要使用 while 语句再次进行判断
4、改进
(1)资源类
- ShareImprove 类
package com.my.communicate;public class ShareImprove {// 初始值private int number;// 加 1 的方法public synchronized void increase() throws InterruptedException {// number 为 0 则等待while (number != 0) {this.wait();}number++;System.out.println(Thread.currentThread().getName() + " " + number);// 通知其他线程this.notifyAll();}// 减 1 的方法public synchronized void decrease() throws InterruptedException {// number 为 0 则等待while (number != 1) {this.wait();}number--;System.out.println(Thread.currentThread().getName() + " " + number);// 通知其他线程this.notifyAll();}
}
(2)多线程测试
- ShareTestImprove 类
package com.my.communicate;public class ShareTestImprove {public static void main(String[] args) {Share share = new Share();Thread thread1 = new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increase();} catch (InterruptedException e) {e.printStackTrace();}}}, "AA");Thread thread2 = new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrease();} catch (InterruptedException e) {e.printStackTrace();}}}, "BB");Thread thread3 = new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increase();} catch (InterruptedException e) {e.printStackTrace();}}}, "CC");Thread thread4 = new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrease();} catch (InterruptedException e) {e.printStackTrace();}}}, "DD");thread1.start();thread2.start();thread3.start();thread4.start();}
}
三、Lock 实现线程间通信
1、需求
- 通过使用两个线程对值(0)进行操作,一个线程加 1,一个线程减 1,交替实现多次
2、具体实现
- LShare 类
(1)资源类
package com.my.communicate;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LShare {// 初始值private int number;// 创建可重入锁private Lock lock;// 创建 Condition 对象private Condition condition;public LShare() {number = 0;lock = new ReentrantLock();condition = lock.newCondition();}// 加 1 的方法public void increase() {lock.lock();try {while (number != 0) {// 等待condition.await();}number++;System.out.println(Thread.currentThread().getName() + " " + number);// 通知其他线程condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}// 减 1 的方法public void decrease() {lock.lock();try {while (number != 1) {// 等待condition.await();}number--;System.out.println(Thread.currentThread().getName() + " " + number);// 通知其他线程condition.signalAll();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}
(2)多线程测试
- LShareTest 类
package com.my.communicate;public class LShareTest {public static void main(String[] args) {LShare lShare = new LShare();Thread thread1 = new Thread(() -> {for (int i = 0; i < 10; i++) {lShare.increase();}}, "AA");Thread thread2 = new Thread(() -> {for (int i = 0; i < 10; i++) {lShare.decrease();}}, "BB");Thread thread3 = new Thread(() -> {for (int i = 0; i < 10; i++) {lShare.increase();}}, "CC");Thread thread4 = new Thread(() -> {for (int i = 0; i < 10; i++) {lShare.decrease();}}, "DD");thread1.start();thread2.start();thread3.start();thread4.start();}
}