结合生活,剖析《生产者消费者模型》-java多线程(一)
博客园的园友们好,看博客园上各位大佬的文章,已陪伴了我程序员职业的三年,
如今自己同样希望能把自己从小白到菜鸟的成长过程分享给大家。不定期更新!!!
首先我本人智商不高,理解问题十分吃力,完全不属于天才的行列,因此学习每一个知识
都喜欢刨根问底,结合生活,彻彻底底理解知识的本质!
进入正题,这篇文章,主要站在一个初学者的角度,结合经典的“生产者消费者模型”,写一个java多线程例子!
首先解释几个概念:
1、#进程:通俗的讲,就是一个程序一次执行的过程。是系统进行资源分配和调度的一个独立单位。
2、#线程:一个进程的生命周期,由一个或若干个线程完成。是CPU调度和分派的基本单位。
3、#并行:同一时间点或者时间段,可以处理超过一个任务的能力。
eg:你正在lol,女朋友来电话,于是你单手操作或者侧头夹住手机,既聊天又打团,这就是你自己并行处理了,
撩妹和打游戏。这个过程,你就类似于cpu,前提你是多核。
4、#并发:主要针对多线程提出的概念。可以在一个时间段,交替执行不同事情的能力。
eg:你正在lol,女朋友来电话,于是你挂机去接电话,然后打完电话回来面对腾讯的裁决。或者你等着打完团,然后
面对女友的生气。这就是并发,你交替执行了不同事情。这个过程,你也类似于cpu,可以不用多核。
二、简单代码实现多线程
2.1模型图
注释:张全蛋经过自己的努力,进入富士康工厂工作,主要负责生产时下流行的iPhoneXs,然后广大果粉在库存充足的情况下购买iPhoneXs。
这个过程共涉及以下角色和过程:
1、生产者(张全蛋)。
2、消费者(广大果粉)。
3、产品(iPhoneXs)。
4、生产产品,购买产品。
2.2 程序展示
2.2.1 产品类
由于我们此次实验过程,主要涉及生产产品,所以我们可以忽略产品本身具有的属性和方法。
1 package com.dcits.weipt; 2 3 /** 4 * 产品实体类 5 * 因我们此次实验是针对生产产品 6 * 所以我们忽略产品本身所具有的方法和属性 7 * @author weipt 8 * @date 20180915*/ 9 public class Product { 10 11 }
2.2.2 工厂类
工厂类用于生产产品,需要注意的是,在java中,生产一个产品,就是new一个产品类的实体对象。
1 package com.dcits.weipt; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 生产产品的工厂类 8 * 生产一个产品则意味着new一个产品对象 9 * 消费一个产品则意味着remove一个对象 10 * @author weipt 11 * @date 20180915*/ 12 13 14 public class ProductFactory { 15 private List<Product> list = new ArrayList<Product>(); //利用list保存对象 16 17 /** 18 * 工厂生产产品的方法 19 * 单位时间生产3个 20 * */ 21 public void makeProduct() { 22 for(int i=0;i<3;i++) { 23 list.add(new Product()); 24 } 25 } 26 27 /** 28 * 工厂消费产品的方法 29 * 单位时间消费1个*/ 30 public void moveProduct() { 31 list.remove(0); 32 } 33 34 /** 35 * 获取产品个数*/ 36 public int getNum() { 37 return list.size(); 38 } 39 40 }
2.2.3 生产者
用于不断调用工厂类,生产产品。每当我们生产一次产品,需要调用notify/notifyAll通知或唤醒消费者来购买。
但当我们生产超过max,需要调用wait,等待消费者购买,减少库存。
1 package com.dcits.weipt; 2 3 /** 4 *生产者实体类 5 * @author weipt 6 * @date 20180915*/ 7 8 public class Producer implements Runnable { 9 private final int MAX_PRODUCT = 30; 10 private ProductFactory pf; 11 12 public Producer(ProductFactory pfIn) { 13 this.pf = pfIn; 14 } 15 16 @Override 17 public void run() { 18 produce(); 19 } 20 21 /** 22 * 生产者生产,产品 23 */ 24 public void produce() { 25 while (true) { 26 synchronized (pf) { 27 if (pf.getNum() >= MAX_PRODUCT) { 28 try { 29 System.out.println("warnning! 库存已满,请稍微再生产!"); 30 pf.wait(); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 }else { 35 pf.makeProduct(); 36 System.out.println("P---》生产者生产了第【" + pf.getNum() + "】个产品"); 37 pf.notifyAll(); 38 } 39 try { 40 Thread.sleep(1000); 41 } catch (InterruptedException e) { 42 // TODO Auto-generated catch block 43 e.printStackTrace(); 44 } 45 } 46 } 47 } 48 }
2.2.4 消费者
用于不断调用工厂类,购买产品。每当我们购买一次产品,需要调用notify/notifyAll通知或唤醒生产者去生产。
但当库存小于min,需要调用wait,等待生产者,增加库存。
1 package com.dcits.weipt; 2 3 /** 4 * 消费者实体类 5 * @author weipt 6 * @date 20180915*/ 7 8 public class Consumer implements Runnable { 9 private final int MIN_PRODUCT = 0; //产品最小值 10 private ProductFactory pf; //产品工厂对象 11 12 /** 13 * 通过构造方法获取产品对象*/ 14 public Consumer(ProductFactory pfIn) { 15 this.pf = pfIn; 16 } 17 18 @Override 19 public void run() { 20 consume(); 21 } 22 23 /** 24 * 消费者从库存中取产品 25 */ 26 public void consume() { 27 while (true) { 28 synchronized (pf) { //因为生产者和消费者都是对产品操作,所以对产品进行加锁 29 if (pf.getNum() <= MIN_PRODUCT) { 30 try { 31 System.out.println("warnning! 库存已空,请稍微再取!");//注意顺序,要放到wait之前 32 pf.wait(); //等待其他线程操作,直到收到其他线程的notify 33 //此处我没有写notify,因为库存缺货,就只能等生产者生产,唤醒其他消费者没有用 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 }else { 38 System.out.println("C---》消费者取走了第【" + pf.getNum() + "】个产品"); 39 pf.moveProduct(); //产品出库 40 pf.notifyAll(); //通知生产者可以继续生产 41 } 42 try { 43 Thread.sleep(1000); //防止日志打印过多,没有可观性 44 } catch (InterruptedException e) { 45 // TODO Auto-generated catch block 46 e.printStackTrace(); 47 } 48 } 49 } 50 } 51 52 }
2.2.5 开始工作
在main方法中,开启消费者和生产者线程。
1 package com.dcits.weipt; 2 3 /** 4 * 公司领导 5 * 用于让整个系统运作起来*/ 6 7 public class HelloThread{ 8 public static void main(String[] args) { 9 ProductFactory pf = new ProductFactory(); 10 Producer p = new Producer(pf); 11 Consumer c = new Consumer(pf); 12 Thread pt = new Thread(p); 13 Thread ct = new Thread(c); 14 pt.start(); //我们可以开启多个消费者或者多个生产者 15 ct.start(); 16 } 17 }
到这里,这篇文章就彻底结束了。
注:如需索要编译好的项目源码可关注公众号mht18391859179(扫描下方二维码),回复:2018091601 免费领取
如果需要交流或者指正,可通过上述公众号,或者email:wpt191@163.com与本人联系。
笨鸟先飞,终生学习
特别鸣谢:
1、感谢胡**,同志的交流与指导。
2、感谢博客园,csdn,知乎等大牛文章的启迪。