线程同步中的“生产者和消费者”模式

过多的线程同步可能会造成死锁

死锁通俗来讲就是两个或者两个以上线程,占用了对方下一步所需要的资源,多个线程僵持都无法结束任务的状态

 

生产者和消费者模式是一个多线程同步的经典案例

它利用信号灯来判断线程是否可以执行,可以避免线程的死锁问题

在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();
	}
}

 

 

输出结果每次运行都不尽相同

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值