简单认识一下线程通信
目录
- 简单认识一下线程通信
- 线程通信定义
- 线程通信模型之一
- 释疑示例
- 案例
- 案例要求
- 案例简单实现
- 拓展
- 等待和唤醒API
- 参考视频
注:线程通信 前提是 线程安全
线程通信定义
当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调或完成某一项任务,并避免无效的资源争夺。
线程通信模型之一
释疑示例
案例
案例要求
3个生产者线程,负责生产包子,每个线程每次只能生产1个包子放在桌子上。
2个消费者线程负责吃包子,每人每次只能从桌子上拿1个包子吃。
案例简单实现
操作模版类
package com.xie.thread.communication;
import java.util.ArrayList;
import java.util.List;
/**
* 容器--桌子,同时也是操作对象模版类 注:当前锁对象用this关键字来指定
* */
public class Desk {
private List<String> list = new ArrayList<>();
/**
* 放包子方法 操作对象:厨师1 厨师1 厨师3
* */
public synchronized void put() {
// 获取当前执行的线程名
String name = Thread.currentThread().getName();
try {
// 判断集合中是否有元素(包子)
if (list.size() == 0) {
list.add(name + "做的肉包子");
System.out.println(name + "做了一个肉包子~~");
// 模拟工作耗时,等待时间
Thread.sleep(2000);
// 唤醒别人,等待自己
this.notifyAll();
this.wait();
}else {
// 发现容器中有元素(包子)
// 唤醒别人,等待自己
this.notifyAll();
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 取(或吃)包子方法 操作对象:吃货1 吃货2
* */
public synchronized void get() {
// 获取当前执行的线程名
String name = Thread.currentThread().getName();
try {
if (list.size() == 1) {
// 取出元素(包子),代表吃了
System.out.println(name + "吃了:" + list.get(0));
list.clear();
// 模拟吃的耗时
Thread.sleep(1000);
// 唤醒别人,等待自己
this.notifyAll();
this.wait();
}else{
// 发现容器中没有元素(包子),空的
// 唤醒别人,等待自己
this.notifyAll();
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试
package com.xie.thread.communication;
/**
* 线程通信---消费者与生产者案例 注:下面的5个线程共用同一把锁!!!
*
* 案例要求:
* 3个生产者线程,负责生产包子,每个线程每次只能生产1个包子放在桌子上
* 2个消费者线程负责吃包子,每人每次只能从桌子上拿1个包子吃
* */
public class ThreadTest {
public static void main(String[] args) {
/**创建操作对象*/
Desk desk = new Desk();
/**
* 创建三个消费者线程,匿名内部类方式创建
* */
new Thread(() -> {
while (true) {
desk.put();
}
}, "厨师1").start();
new Thread(() -> {
while (true) {
desk.put();
}
}, "厨师2").start();
new Thread(() -> {
while (true) {
desk.put();
}
}, "厨师3").start();
/**
* 创建两个生产者线程,匿名内部类方式创建
* */
new Thread(() -> {
while (true) {
desk.get();
}
}, "吃货1").start();
new Thread(() -> {
while (true) {
desk.get();
}
}, "吃货2").start();
}
}
拓展
等待和唤醒API
注:上述方法应该使用 当前同步锁对象 进行调用。
参考视频
黑马磊哥