zoukankan      html  css  js  c++  java
  • Java多线程生产者消费者模式(二)之死锁与虚假唤醒

    前提:

    当只有一个生产者与消费者,也就是只有两个线程时,唤醒的永远时对方线程。

    当只有一个生产者时和两个消费者,或者两个生产者与两个消费者时,唤醒的就是所有线程了,这样就会产生死锁和虚假唤醒。

    多线程对多线程,必会有死锁和虚假唤醒问题。

    比如下面的代码,初看正常,刚运行几次也正常,但多运行几次就出现问题了。大概率出现的问题是死锁,程序无穷无尽的卡住了。

    我们来理解这个问题:C吃完了包子,此时包子为0个,C唤醒了厨师A和吃货B这两个线程。

    因为A和B都醒了,A作为厨师去生产包子了。

    B作为吃货一看包子为0个,就等着,而且是死等;当厨师A做完包子,唤醒大家去吃包子时,B还在死等,而此时虽然C也被唤醒了。

    但C一看被锁住了,而这个吃货B却死等着不让,于是B和C就僵持住了。

    相当于:餐厅里只有一个位置,B一看没有包子了,就死等;在B死等的时候,厨师A做好了一个包子并叫大家去吃。

    C高兴的去吃包子,但位置被B占用了,而且B不知道厨师做好了包子,一直死等,一直占着位置,这样一来,就卡在B这里了,C和A都急死了。

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Bun {
        public static void main(String[] args) {
            SteamedBun bun = new SteamedBun();
    
            new Thread(() -> {
                for (int i = 0; i < 5; i++) {
                    try {
                        bun.produce();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            }, "厨师A").start();//定义了一个厨师线程,生长包子
            new Thread(() -> {
    
                for (int i = 0; i < 5; i++) {
                    try {
                        bun.eat();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "吃货B").start();//定义了一个吃货线程,吃包子
    
    
            new Thread(() -> {
    
                for (int i = 0; i < 5; i++) {
                    try {
                        bun.eat();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "吃货C").start();//定义了一个吃货线程,吃包子
    
        }
    }
    
    
    class SteamedBun {
        private int num = 0;//包子数量
    
        //生产者,包子铺的厨师
        public synchronized void produce() throws InterruptedException {
            while (num != 0) {
                this.wait();
            }
            num++;
            System.out.println(Thread.currentThread().getName() + "生产了1个包子");
            this.notifyAll();
        }
    
    
        //消费者-->吃包子的吃货
        public synchronized void eat() throws InterruptedException {
            while (num == 0) {
                this.wait();
            }
            num--;
            System.out.println(Thread.currentThread().getName() + "吃了1个包子");
            this.notifyAll();
            System.out.println(Thread.currentThread().getName()+"叫厨师去做包子");
        }
    }

    以上代码运行结果偶尔正常,偶尔发生本文开头提到的死锁。

    下面的结果只有14行,就卡在14行不动了。

    厨师A生产了1个包子
    吃货B吃了1个包子
    吃货B叫厨师去做包子
    厨师A生产了1个包子
    吃货B吃了1个包子
    吃货B叫厨师去做包子
    厨师A生产了1个包子
    吃货C吃了1个包子
    吃货C叫厨师去做包子
    厨师A生产了1个包子
    吃货B吃了1个包子
    吃货B叫厨师去做包子
    厨师A生产了1个包子
    吃货C吃了1个包子
    吃货C叫厨师去做包子
  • 相关阅读:
    PHP全路径无限分类原理
    SecureCRT上传bash: rz: command not found
    Android利用Fiddler进行网络数据抓包【怎么跟踪微信请求】
    Creating the Help Page in ASP.NET Web API
    Windows上怎么安装ELK
    安装ELK
    How to kill a process on a port on linux 怎么杀死 关掉一个端口
    How to install Mysql in the CentOS
    Hadoop: Setup Maven project for MapReduce in 5mn
    Install Ambari 2.2.0 from Public Repositories(Hadoop)
  • 原文地址:https://www.cnblogs.com/majestyking/p/12451639.html
Copyright © 2011-2022 走看看