线程通信
图片来源:尚学堂ppt




线程通信模型
管程法
案例一 没有加线程通信:
情景设置:工厂(生产者)生产馒头,仓库(缓冲器)存储馒头,商店(消费者)从仓库取走馒头,加线程安全,不加线程通信
package _20191206;
/**
* 生产者消费者模型:管程法
* @author TEDU
* 情景设置:工厂(生产者)生产馒头,仓库(缓冲器)存储馒头,商店(消费者)从仓库取走馒头,加线程安全,不加线程通信
*/
public class CoTest01 {
public static void main(String[] args) {
Storage storage = new Storage();
new Productor(storage).start();
new Consumer(storage).start();
}
}
//工厂:不止一家,多线程
class Productor extends Thread{
private Storage stor;
public Productor(Storage stor) {
this.stor = stor;
}
public void run() {
for(int i = 0; i < 200;i++) {
stor.push(new SteamedBun(i));
}
}
}
//仓库:仓库就是容器,因为仓库有固定的大小,所以考虑用数组来做
class Storage{
private SteamedBun[] buns = new SteamedBun[10];//仓库的大小是固定的
private int count = 0;//仓库数组下标
//入仓
public synchronized void push(SteamedBun bun) {
buns[count] = bun;
System.out.println("存入第"+buns[count].getId()+"个馒头 count:"+count);
count++;
}
//出仓
public synchronized void pop() {
count--;
System.out.println("取出第"+buns[count].getId()+"个馒头 count:"+count);
}
}
//商店:不止一家(多线程)
class Consumer extends Thread{
private Storage stor;
public Consumer(Storage stor) {
this.stor = stor;
}
public void run() {
for(int i=0;i<200;i++) {
stor.pop();
}
}
}
//商品:馒头
class SteamedBun {
private int id;
public SteamedBun(int id) {
this.id = id+1;
}
public int getId() {
return id;
}
}
运行结果:
存入第1个馒头 count:0 存入第2个馒头 count:1 存入第3个馒头 count:2 存入第4个馒头 count:3 存入第5个馒头 count:4 存入第6个馒头 count:5 存入第7个馒头 count:6 存入第8个馒头 count:7 存入第9个馒头 count:8 存入第10个馒头 count:9 取出第10个馒头 count:9 取出第9个馒头 count:8 Exception in thread "Thread-0" 取出第8个馒头 count:7 取出第7个馒头 count:6 取出第6个馒头 count:5 取出第5个馒头 count:4 取出第4个馒头 count:3 取出第3个馒头 count:2 取出第2个馒头 count:1 取出第1个馒头 count:0 Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: -1 at _20191206.Storage.pop(CoTest01.java:42) at _20191206.Consumer.run(CoTest01.java:55) java.lang.ArrayIndexOutOfBoundsException: 10 at _20191206.Storage.push(CoTest01.java:35) at _20191206.Productor.run(CoTest01.java:24)
可以看到,取馒头时,取到仓库下标0后,再试图取已经没有可以取得馒头了,指针越界异常报出。
这个时候,我们需要引入wait与notify方法,在仓库存满时,停止生产馒头,仓库空时,停止取馒头。
案例二:加了线程通信
在案例一的基础上,我们加上wait与notifyAll方法,通过判断生产与取货的时机,来停止生产与停止取货(wait()方法)以及继续生产继续取货(notifyAll()方法)。
-
this.wait() 停止当前线程
- this.notifyAll() 激活其它线程
//入仓
public synchronized void push(SteamedBun bun) {
//何时停止存?调用wait
if(count == buns.length) {//如果存到下标为仓库的大小,说明满了,停止存
try {
this.wait();//停止当前线程
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
buns[count] = bun;
System.out.println("存入第"+buns[count].getId()+"个馒头 count:"+count);
count++;
//一旦开始存,就可以通知商家来取
this.notifyAll();//激活其它线程
}
//出仓
public synchronized void pop() {
//何时出仓? 调用wait
if(count == 0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
count--;
System.out.println("取出第"+buns[count].getId()+"个馒头 count:"+count);
//一旦开始取,仓库就不是满的,就要通知生产者生产
this.notifyAll();
}
运行结果:

信号灯法
适用:生产1个取1个的模式
情景设置:一个路口有一个斑马线信号灯,当信号灯红色,人停车走,当信号灯绿色,车停人走。
package _20191206;
/**
* 生产者消费者模式:信号灯法
* @author TEDU
* 情景设置:一个路口有一个斑马线信号灯,当信号灯红色,人停车走,当信号灯绿色,车停人走。
* 适用:生产1个取1个的模式
*/
public class CoTest02 {
public static void main(String[] args) {
//来一个信号灯
SignalLight light = new SignalLight("斑马线信号灯",true);
new Walker(light).start();
new Car(light).start();
}
}
//行人
class Walker extends Thread{
private SignalLight light;
public Walker(SignalLight light) {
this.light = light;
}
public void run() {
for (int i = 0; i < 10; i++) {
light.forWalker();
}
}
}
//车
class Car extends Thread{
private SignalLight light;
public Car(SignalLight light) {
this.light = light;
}
public void run() {
for (int i = 0; i < 10; i++) {
light.forCar();
}
}
}
//交通灯
class SignalLight{
private boolean flag = true;
//flag 值为 True 表示绿灯,行人走。值false表示红灯,车走。
private String name;
public SignalLight(String name,boolean b) {
this.flag = b;
this.name = name;
}
public synchronized void forWalker() {
//查看信号灯
if(!flag) {//红灯等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//绿灯走你
System.out.println(name+":绿-行人正在过马路...");
this.notifyAll();
flag = !flag;
}
public synchronized void forCar() {
//查看信号灯
if(flag) {//绿灯等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//红灯走你
System.out.println(name+":红-车正在驶过...");
this.notifyAll();
flag = !flag;
}
}
结果:
斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过... 斑马线信号灯:绿-行人正在过马路... 斑马线信号灯:红-车正在驶过...