zoukankan      html  css  js  c++  java
  • 两个线程一个生产者个一个消费者

    需求情景

    • 两个线程,一个负责生产,一个负责消费,生产者生产一个,消费者消费一个

    涉及问题

    • 同步问题:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用标记或加锁机制
    • wait() / nofity() 方法是基类Object的两个方法,也就意味着所有Java类都会拥有这两个方法,这样,我们就可以为任何对象实现同步机制。
    • wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。
    • notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

    代码实现(共三个类和一个main方法的测试类)

    Resource.java

    复制代码
    /**
     * Created by yuandl on 2016-10-11./**
     * 资源
     */
    public class Resource {
        /*资源序号*/
        private int number = 0;
        /*资源标记*/
        private boolean flag = false;
    
        /**
         * 生产资源
         */
        public synchronized void create() {
            if (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
                try {
                    wait();//让生产线程等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number++;//生产一个
            System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
            flag = true;//将资源标记为已经生产
            notify();//唤醒在等待操作资源的线程(队列)
        }
    
        /**
         * 消费资源
         */
        public synchronized void destroy() {
            if (!flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println(Thread.currentThread().getName() + "消费者****" + number);
    
            flag = false;
            notify();
        }
    }
    复制代码

    Producer.java

    复制代码
    /**
     * Created by yuandl on 2016-10-11.
     *
     /**
     * 生产者   http://www.manongjc.com
     */
    
    public class Producer implements Runnable {
        private Resource resource;
    
        public Producer(Resource resource) {
            this.resource = resource;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                resource.create();
            }
    
        }
    }
    复制代码

    Consumer.java

    复制代码
    /**
     * 消费者
     */
    public class Consumer implements Runnable {
        private Resource resource;
    
        public Consumer(Resource resource) {
            this.resource = resource;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                resource.destroy();
            }
    
        }
    }
    复制代码

    ProducerConsumerTest.java

    复制代码
    /**
     * Created by yuandl on 2016-10-11.
     */
    public class ProducerConsumerTest {
        public static void main(String args[]) {
            Resource resource = new Resource();
            new Thread(new Producer(resource)).start();//生产者线程
            new Thread(new Consumer(resource)).start();//消费者线程
    
        }
    
    }
    复制代码

    打印结果:

    复制代码
    Thread-0生产者------------1
    Thread-1消费者****1
    Thread-0生产者------------2
    Thread-1消费者****2
    Thread-0生产者------------3
    Thread-1消费者****3
    Thread-0生产者------------4
    Thread-1消费者****4
    Thread-0生产者------------5
    Thread-1消费者****5
    Thread-0生产者------------6
    Thread-1消费者****6
    Thread-0生产者------------7
    Thread-1消费者****7
    Thread-0生产者------------8
    Thread-1消费者****8
    Thread-0生产者------------9
    Thread-1消费者****9
    Thread-0生产者------------10
    Thread-1消费者****10
    复制代码

    以上打印结果可以看出没有任何问题

    多个线程,多个生产者和多个消费者的问题

    需求情景

    • 四个线程,两个个负责生产,两个个负责消费,生产者生产一个,消费者消费一个

    涉及问题

    • notifyAll()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的所有线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

    再次测试代码

    ProducerConsumerTest.java

    复制代码
    **
     * Created by yuandl on 2016-10-11.
     */
    public class ProducerConsumerTest {
        public static void main(String args[]) {
            Resource resource = new Resource();
            new Thread(new Consumer(resource)).start();//生产者线程
            new Thread(new Consumer(resource)).start();//生产者线程
            new Thread(new Producer(resource)).start();//消费者线程
            new Thread(new Producer(resource)).start();//消费者线程
    
        }
    
    }
    复制代码

    运行结果:

    复制代码
    Thread-0生产者------------100
    Thread-3消费者****100
    Thread-0生产者------------101
    Thread-3消费者****101
    Thread-2消费者****101
    Thread-1生产者------------102
    Thread-3消费者****102
    Thread-0生产者------------103
    Thread-2消费者****103
    Thread-1生产者------------104
    Thread-3消费者****104
    Thread-1生产者------------105
    Thread-0生产者------------106
    Thread-2消费者****106
    Thread-1生产者------------107
    Thread-3消费者****107
    Thread-0生产者------------108
    Thread-2消费者****108
    Thread-0生产者------------109
    Thread-2消费者****109
    Thread-1生产者------------110
    Thread-3消费者****110
    复制代码

    通过以上打印结果发现问题

    • 101生产了一次,消费了两次
    • 105生产了,而没有消费

    原因分析

    • 当两个线程同时操作生产者生产或者消费者消费时,如果有生产者或者的两个线程都wait()时,再次notify(),由于其中一个线程已经改变了标记而另外一个线程再次往下直接执行的时候没有判断标记而导致的。
    • if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。

    解决方案

    • while判断标记,解决了线程获取执行权后,是否要运行!也就是每次wait()后再notify()时先再次判断标记

    代码改进(Resource中的if->while) 

    Resource.java

    复制代码
    /**
     * Created by yuandl on 2016-10-11./**
     * 资源
     */
    public class Resource {
        /*资源序号*/
        private int number = 0;
        /*资源标记*/
        private boolean flag = false;
    
        /**
         * 生产资源
         */
        public synchronized void create() {
            while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
                try {
                    wait();//让生产线程等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number++;//生产一个
            System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
            flag = true;//将资源标记为已经生产
            notify();//唤醒在等待操作资源的线程(队列)
        }
    
        /**
         * 消费资源
         */
        public synchronized void destroy() {
            while (!flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println(Thread.currentThread().getName() + "消费者****" + number);
    
            flag = false;
            notify();
        }
    }
    复制代码

    再次发现问题

    • 打印到某个值比如生产完74,程序运行卡死了,好像锁死了一样。

    原因分析

    • notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致”死锁”。

    解决方案

    • notifyAll解决了本方线程一定会唤醒对方线程的问题。

    最后代码改进(Resource中的notify()->notifyAll()) 

    Resource.java

    复制代码
    /**
     * Created by yuandl on 2016-10-11./**
     * 资源
     */
    public class Resource {
        /*资源序号*/
        private int number = 0;
        /*资源标记*/
        private boolean flag = false;
    
        /**
         * 生产资源
         */
        public synchronized void create() {
            while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
                try {
                    wait();//让生产线程等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            number++;//生产一个
            System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
            flag = true;//将资源标记为已经生产
            notifyAll();//唤醒在等待操作资源的线程(队列)
        }
    
        /**
         * 消费资源
         */
        public synchronized void destroy() {
            while (!flag) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            System.out.println(Thread.currentThread().getName() + "消费者****" + number);
    
            flag = false;
            notifyAll();
        }
    }
    复制代码

    运行结果:

    Thread-0生产者------------412
    Thread-2消费者****412
    Thread-0生产者------------413
    Thread-3消费者****413
    Thread-1生产者------------414
    Thread-2消费者****414
    Thread-1生产者------------415
    Thread-2消费者****415
    Thread-0生产者------------416
    Thread-3消费者****416
    Thread-1生产者------------417
    Thread-3消费者****417
    Thread-0生产者------------418
    Thread-2消费者****418
    Thread-0生产者------------419
    Thread-3消费者****419
    Thread-1生产者------------420
    Thread-2消费者****420
  • 相关阅读:
    scrapy爬虫系列之三--爬取图片保存到本地
    scrapy爬虫系列之四--爬取列表和详情
    python3.7.2 ssl版本过低导致pip无法使用的问题
    python3安装后无法使用退格键的问题
    位运算符和unity Layers
    unity常用小知识点
    unity -- Time类(持续更新中)
    随便说说 post-processing
    unity图片后期处理
    顶点/片元 shader 总结
  • 原文地址:https://www.cnblogs.com/fengmao/p/8609197.html
Copyright © 2011-2022 走看看