线程协作-生产者/消费者模式
来源:互联网 发布:单片机usb 串口 编辑:程序博客网 时间:2024/06/08 08:01
上一篇讲述了线程的互斥(同步),但是在很多情况下,仅仅同步是不够的,还需要线程与线程协作(通信),生产者/消费者模式是一个经典的线程同步以及通信的模型。
假设有这样一种情况,有一个盘子,盘子里只能放一个鸡蛋,A线程专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B线程专门从盘子里取鸡蛋,如果盘子里没鸡蛋,则一直等到盘子里有鸡蛋。这里盘子是一个互斥区,每次放鸡蛋是互斥的,每次取鸡蛋也是互斥的,A线程放鸡蛋,如果这时B线程要取鸡蛋,由于A没有释放锁,B线程处于等待状态,进入阻塞队列,放鸡蛋之后,要通知B线程取鸡蛋,B线程进入就绪队列,反过来,B线程取鸡蛋,如果A线程要放鸡蛋,由于B线程没有释放锁,A线程处于等待状态,进入阻塞队列,取鸡蛋之后,要通知A线程放鸡蛋,A线程进入就绪队列。我们希望当盘子里有鸡蛋时,A线程阻塞,B线程就绪,盘子里没鸡蛋时,A线程就绪,B线程阻塞,代码如下:
- import java.util.ArrayList;
- import java.util.List;
- /** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */
- public class Plate {
- /** 装鸡蛋的盘子 */
- List<Object> eggs = new ArrayList<Object>();
- /** 取鸡蛋 */
- public synchronized Object getEgg() {
- while (eggs.size() ==0) {
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- Object egg = eggs.get(0);
- eggs.clear();// 清空盘子
- notify();// 唤醒阻塞队列的某线程到就绪队列
- System.out.println("拿到鸡蛋");
- return egg;
- }
- /** 放鸡蛋 */
- public synchronizedvoid putEgg(Object egg) {
- while (eggs.size() > 0) {
- try {
- wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- eggs.add(egg);// 往盘子里放鸡蛋
- notify();// 唤醒阻塞队列的某线程到就绪队列
- System.out.println("放入鸡蛋");
- }
- static class AddThreadextends Thread {
- private Plate plate;
- private Object egg =new Object();
- public AddThread(Plate plate) {
- this.plate = plate;
- }
- public void run() {
- plate.putEgg(egg);
- }
- }
- static class GetThreadextends Thread {
- private Plate plate;
- public GetThread(Plate plate) {
- this.plate = plate;
- }
- public void run() {
- plate.getEgg();
- }
- }
- public staticvoid main(String args[]) {
- Plate plate = new Plate();
- for(int i =0; i < 10; i++) {
- new Thread(new AddThread(plate)).start();
- new Thread(new GetThread(plate)).start();
- }
- }
- }
import java.util.ArrayList;import java.util.List;/** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */public class Plate {/** 装鸡蛋的盘子 */List<Object> eggs = new ArrayList<Object>();/** 取鸡蛋 */public synchronized Object getEgg() {while (eggs.size() == 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}Object egg = eggs.get(0);eggs.clear();// 清空盘子notify();// 唤醒阻塞队列的某线程到就绪队列System.out.println("拿到鸡蛋");return egg;}/** 放鸡蛋 */public synchronized void putEgg(Object egg) {while (eggs.size() > 0) {try {wait();} catch (InterruptedException e) {e.printStackTrace();}}eggs.add(egg);// 往盘子里放鸡蛋notify();// 唤醒阻塞队列的某线程到就绪队列System.out.println("放入鸡蛋");}static class AddThread extends Thread {private Plate plate;private Object egg = new Object();public AddThread(Plate plate) {this.plate = plate;}public void run() {plate.putEgg(egg);}}static class GetThread extends Thread {private Plate plate;public GetThread(Plate plate) {this.plate = plate;}public void run() {plate.getEgg();}}public static void main(String args[]) {Plate plate = new Plate();for(int i = 0; i < 10; i++) {new Thread(new AddThread(plate)).start();new Thread(new GetThread(plate)).start();}}}输出结果:
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
- 放入鸡蛋
- 拿到鸡蛋
放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋放入鸡蛋拿到鸡蛋8 l程序开始,A线程判断盘子是否为空,放入一个鸡蛋,并且唤醒在阻塞队列的一个线程,阻塞队列为空;假设CPU又调度了一个A线程,盘子非空,执行等待,这个A线程进入阻塞队列;然后一个B线程执行,盘子非空,取走鸡蛋,并唤醒阻塞队列的A线程,A线程进入就绪队列,此时就绪队列就一个A线程,马上执行,放入鸡蛋;如果再来A线程重复第一步,在来B线程重复第二步,整个过程就是生产者(A线程)生产鸡蛋,消费者(B线程)消费鸡蛋。
前段时间看了张孝祥老师线程的视频,讲述了一个其学员的面试题,也是线程通信的,在此也分享一下。
题目:子线程循环10次,主线程循环100次,如此循环100次,好像是空中网的笔试题。
- public class ThreadTest2 {
- public staticvoid main(String[] args) {
- final Business business =new Business();
- new Thread(new Runnable() {
- @Override
- public void run() {
- threadExecute(business, "sub");
- }
- }).start();
- threadExecute(business, "main");
- }
- public staticvoid threadExecute(Business business, String threadType) {
- for(int i =0; i < 100; i++) {
- try {
- if("main".equals(threadType)) {
- business.main(i);
- } else {
- business.sub(i);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class Business {
- private boolean bool =true;
- public synchronizedvoid main(int loop)throws InterruptedException {
- while(bool) {
- this.wait();
- }
- for(int i =0; i < 100; i++) {
- System.out.println("main thread seq of " + i +", loop of " + loop);
- }
- bool = true;
- this.notify();
- }
- public synchronizedvoid sub(int loop)throws InterruptedException {
- while(!bool) {
- this.wait();
- }
- for(int i =0; i < 10; i++) {
- System.out.println("sub thread seq of " + i +", loop of " + loop);
- }
- bool = false;
- this.notify();
- }
- }
public class ThreadTest2 {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {@Overridepublic void run() {threadExecute(business, "sub");}}).start();threadExecute(business, "main");}public static void threadExecute(Business business, String threadType) {for(int i = 0; i < 100; i++) {try {if("main".equals(threadType)) {business.main(i);} else {business.sub(i);}} catch (InterruptedException e) {e.printStackTrace();}}}}class Business {private boolean bool = true;public synchronized void main(int loop) throws InterruptedException {while(bool) {this.wait();}for(int i = 0; i < 100; i++) {System.out.println("main thread seq of " + i + ", loop of " + loop);}bool = true;this.notify();}public synchronized void sub(int loop) throws InterruptedException {while(!bool) {this.wait();}for(int i = 0; i < 10; i++) {System.out.println("sub thread seq of " + i + ", loop of " + loop);}bool = false;this.notify();}}大家注意到没有,在调用wait方法时,都是用while判断条件的,而不是if,在wait方法说明中,也推荐使用while,因为在某些特定的情况下,线程有可能被假唤醒,使用while会循环检测更稳妥
- 线程协作-生产者/消费者模式
- Java线程(三):线程协作-生产者/消费者模式
- Java线程(三):线程协作-生产者/消费者模式
- Java线程(三):线程协作-生产者/消费者模式
- Java线程(三):线程协作-生产者/消费者模式
- Java线程(2)线程协作-生产者/消费者模式
- 线程协作-生产者/消费者问题
- 线程协作-生产者/消费者问题
- 线程(三)线程协作生产者与消费者
- 线程--生产者消费者模式
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- Java线程:并发协作-生产者消费者模型
- uafxcwd.lib(afxglobals.obj) : error LNK2001: 无法解析的外部符号 _IID_IWICImagingFactory
- [图]谷歌街景地图如何生成:步行记录者生活记录
- 百度 软件研发工程师笔试题 2013/10/13 广州考场
- 线程同步synchronized和volatile
- 线程安全与不安全
- 线程协作-生产者/消费者模式
- Timer和TimerTask
- 线程池
- Android中Context详解 ---- 你所不知道的Context
- Callable和Future
- ubuntu安装配置samba服务
- 深拷贝和浅拷贝
- 锁对象Lock-同步问题更完美的处理方式
- Jdbc中的Statement 和PreparedStatement