zoukankan      html  css  js  c++  java
  • 结合生活,剖析《生产者消费者模型》-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,知乎等大牛文章的启迪。

  • 相关阅读:
    tornado+websocket+mongodb实现在线视屏文字聊天
    mongoexport 导出需要授权数据库中的集合 报错 Authentication failed.
    nginx日志中添加请求的response日志
    SSE(Server-sent events)技术在web端消息推送和实时聊天中的使用
    RESTful接口设计原则和优点
    一次请求中,经过 nginx+uWSGI+flask应用程序搭建服务的执行过程
    项目中记录影响性能的缓慢数据库查询
    macos Item2 添加 Shell Integration (ftp传输)
    windows安装 阿里云的Fun工具
    windows10安装docker[含百度网盘docker安装包]
  • 原文地址:https://www.cnblogs.com/weipt0106/p/9653215.html
Copyright © 2011-2022 走看看