过多的线程同步可能会造成死锁
死锁通俗来讲就是两个或者两个以上线程,占用了对方下一步所需要的资源,多个线程僵持都无法结束任务的状态
生产者和消费者模式是一个多线程同步的经典案例
它利用信号灯来判断线程是否可以执行,可以避免线程的死锁问题
在java中,生产者与消费者模式是必须通过同步synchronized才可以实现的
下面的代码中有两种不近相同的该模式代码展示(Movie,Movie2),但是道理都是一样的
我们需要两个Runnable实现类用来创建线程Player,Watcher,一个Moive类作为共同竞争的资源,以及测试类App
其中这两个实现类都共同享有这个资源,那么就需要实现类做到以下两点
1.私有属性-资源类对象
2.带参构造方法-参数为资源类对象
/**
* 生产者
*/
public class Player implements Runnable {
private Movie2 m ;
public Player(Movie2 m) {
super();
this.m = m;
}
@Override
public void run() {
for(int i=0;i<5;i++){
m.play();
}
}
}
/**
* 消费者
*/
public class Watcher implements Runnable {
private Movie2 m ;
public Watcher(Movie2 m) {
super();
this.m = m;
}
@Override
public void run() {
for(int i=0;i<5;i++){
m.watch();
}
}
}
/**
* 测试类
*/
public class App {
public static void main(String[] args) {
//共同的资源
Movie2 m = new Movie2();
//多线程
Player p = new Player(m);
Watcher w = new Watcher(m);
new Thread(p).start();
new Thread(w).start();
}
}
接下来就是核心代码,有几个要点需要注意
1.信号灯法的信号灯说白了也就是资源类的一个属性,它可以是布尔类型也可以是int类型
2.这里用到了Object类的两个方法 wait(),notify()/notifyAll()
/**
* 资源类
*/
public class Movie {
private String pic = "Candy";
// flag -->T 生产生产,消费者等待 ,生产完成后通知消费
// flag -->F 消费者消费 生产者等待, 消费完成后通知生产
private boolean flag = true;
/**
* 生产者生产,this指Player对象
*
* @throws InterruptedException
*/
public synchronized void play() {
if (!flag) {
// 生产者等待
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 生产者---生产
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("生产了:" + pic);
this.pic = pic;
// 通知消费
this.notify();
// 生产者停下
this.flag = false;
}
/**
* 消费者,this指Watcher对象
*
* @throws InterruptedException
*/
public synchronized void watch() {
if (flag) {
// 消费者等待
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 消费者---开始消费
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("消费了" + pic);
// 通知生产者
this.notifyAll();
// 消费停止
this.flag = true;
}
}
之前的资源类中作为信号灯的是一个布尔变量,从输出结果中也可以看出用布尔变量做为信号灯有一个缺点
那就是生产者和消费者是同步的,生产一个消费一个,这与现实生活太不吻合了
所以在Movie2类中,加入了List<Goods>,那么此时信号灯就是list的实际长度了
public class Goods {
public String name;
public Goods(String name){
this.name=name;
}
}
/**
* 资源类二
*/
import java.util.ArrayList;
import java.util.List;
public class Movie2 {
private int flag = 0;
private int maxListSize = 10;
private List<Goods> goodsList;
public Movie2() {
goodsList = new ArrayList<Goods>();
}
// 只要没有超过最大限制,生产者一直生产
public synchronized void play() {
if (maxListSize <= goodsList.size()) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//将生产的物质添加到list中
Goods g = new Goods("candy");
goodsList.add(g);
System.out.println("creat--->" + (++flag) + g.name);
//唤醒消费者
this.notify();
}
public synchronized void watch() {
//只要有物资,消费者就可以消费
if (goodsList.size() <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("use---" + (flag--)+ goodsList.get(goodsList.size() - 1).name);
//从list中删除被消费的物资
goodsList.remove(goodsList.size() - 1);
//唤醒生产者
this.notify();
}
}
输出结果每次运行都不尽相同