一、线程间通信
1、概述
- 线程间通信的模型有两种:共享内存和消息传递
2、多线程编程步骤(中)
-
创建资源类,在资源类中创建属性和操作方法
-
在资源类操作方法进行判断、操作、通知
-
创建多个线程,调用资源类中的操作方法
二、synchronized 实现线程间通信
1、需求
- 通过使用两个线程对值(0)进行操作,一个线程加 1,一个线程减 1,交替实现多次
2、具体实现
(1)资源类
- Share 类
java
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 类
java
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 类
java
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 类
java
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)资源类
java
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 类
java
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();
}
}