• 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)
  • 相关阅读:
    (转)swc使用
    (转)AS3中的反射相关
    AS获取url参数
    (转)深入理解Flash Player的安全域(Security Domains)
    (转)html<embed>标签和url向Flash传flashvars值
    (转)ApplicationDomain
    灰度发布
    (转)flex中使用swc实现更好的界面代码分离
    (转)深入理解Flash Player的应用程序域(Application Domains)
    嵌入式系统可执行文件格式
  • 原文地址:https://www.cnblogs.com/linkworld/p/7457203.html
Copyright © 2020-2023  润新知