zoukankan      html  css  js  c++  java
  • Java 多线程通信之多生产者/多消费者

    // 以生产和消费烤鸭为例
    class Resource
    {
        private String name;
        private int count = 1; // 记录烤鸭的编号
        private boolean flag = false;
    
        public synchronized void set(String name)
        {
            if(flag)
                try{this.wait();}catch(InterruptedException e){}
            this.name = name + count;
            count++;
            System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
            flag = true;
            this.notify();
        }
    
        public synchronized void out()
        {
            if(!flag)
                try{this.wait();}catch(InterruptedException e){}
            Sytem.out.println(Thread.currentThread().getName()+ "...消费者.."+ this.name);
            flag = false;
            this.notify();
        }
    }
    
    class Producer implements Runnable
    {
        Resource r;
        Producer(Resource r)
        {
            this.r = r;
        }
    
        public void run()
        {
            while(true)
            {
                r.set("烤鸭");
            }
        }
    }
    
    class Consumer implements Runnable
    {
        Resource r;
        Consumer(Resource r)
        {
            this.r = r;
        }
        public void run()
        {
            while(true)
            {
                r.out();
            }
        }
    }
    
    class ProducerConsumerDemo
    {
        public static void main(String[] args)
        {
            // 创建资源
            Resource r = new Resource();
    
            // 创建任务
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);
    
            // 多生产者
            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
    
            // 多消费者
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
            Thread t5 = new Thread(con);
    
            // 开启线程
            t0.start();
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
        }
    }
    

    出现错误的两种情况:

    1. 线程安全问题(虚假唤醒): 线程1 生产的烤鸭被线程3 和线程5 两个线程同时消费
      • if 只能判断标记一次, 会导致不该运行的线程运行了, 出现数据错误的情况
      • while 可以多次判断标记, 解决了线程获取执行权后, 是否要运行的问题!

    1. 死锁
      • notify() 一次只能唤醒一个线程, 如果本方唤醒类本方, 没有意义. 而且 while 判断标记多次,
        会导致死锁.
      • notifyAll() 解决了本方线程一定会唤醒对方线程的问题.
    // 升级版代码
    class Resource
    {
        private String name;
        private int count = 1; // 记录烤鸭的编号
        private boolean flag = false;
    
        public synchronized void set(String name)
        {
            // 将 if 换为 while, 线程从冻结状态被唤醒后,需要判断 flag 标记之后, 确定是否继续生产"烤鸭"
            while(flag)  
                try{this.wait();}catch(InterruptedException e){}
            this.name = name + count;
            count++;
            System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
            flag = true;
            this.notifyAll(); // 肯定会唤醒对方的线程, 解决了死锁问题
        }
    
        public synchronized void out()
        {
            while(!flag)
                try{this.wait();}catch(InterruptedException e){}
            Sytem.out.println(Thread.currentThread().getName()+ "...消费者.."+ this.name);
            flag = false;
            this.notifyAll();
        }
    }
    
    class Producer implements Runnable
    {
        Resource r;
        Producer(Resource r)
        {
            this.r = r;
        }
    
        public void run()
        {
            while(true)
            {
                r.set("烤鸭");
            }
        }
    }
    


    - [JavaSE 基础视频(毕向东)](https://www.bilibili.com/video/av3106510/#page=4)
  • 相关阅读:
    jxl读和取Excel文件
    Studio for WPF:定制 C1WPFChart 标记
    为C1Chart for WPF添加自定义标题、坐标轴单位标签以及旋转坐标轴注释
    自定义饼图(PieChart)各个PieSlice的外观
    vue组件
    vue双向绑定
    第一个Vue程序
    vue入门介绍
    js生成随机固定长度字符串的简便方法
    JavaScript 函数式编程读书笔记1
  • 原文地址:https://www.cnblogs.com/linkworld/p/7457203.html
Copyright © 2011-2022 走看看