1. 程式人生 > 實用技巧 >多Product多Consumer之間的通訊導致出現程式假死的原因分析

多Product多Consumer之間的通訊導致出現程式假死的原因分析

多Product多Consumer之間的通訊導致出現程式假死的原因分析

繼續上篇文章,我們來吧單個生產者消費者改成多個生產者消費者,這裡使用java8的Stream,程式碼如下:

/**
 * @program: ThreadDemo
 * @description: 執行緒通訊(生產者-消費者)
 * @author: [email protected]
 * @create: 2020-09-06
 */
public class ProduceConsumerVersion2 {
    private int i = 0;
    private final Object LOCK = new Object();
    /**
     * 是否已經消費
     */
    private volatile boolean isProduced = false;

    public void produce() {
        synchronized (LOCK) {
            if (isProduced) {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("P->" + (++i));
                LOCK.notify();
                isProduced = true;
            }
        }
    }

    public void consume() {
        synchronized (LOCK) {
            if (isProduced) {
                System.out.println("C->" + i);
                LOCK.notify();
                isProduced = false;
            } else {
                try {
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        ProduceConsumerVersion2 pc = new ProduceConsumerVersion2();

        // 多個生產者、消費者執行緒執行時,會產生一些問題
        Stream.of("P1", "P2").forEach(n ->
                new Thread(() -> {
                    while (true) {
                        pc.produce();
                    }
                }, n).start()
        );
        Stream.of("C1", "C2").forEach(n ->
                new Thread(() -> {
                    while (true) {
                        pc.consume();
                    }
                }, n).start()
        );
    }
}

執行效果如下:

可以看到阻塞了,那麼是不是有死鎖了呢,jstack來查一下:

可以看到沒有死鎖,那是為什麼會卡住呢?下面增加一些除錯語句用來定位跟蹤,我把程式碼貼出來,有興趣的朋友在大腦裡分析一下,本地再執行一下驗證自己的想法,程式碼如下:

public class ProduceConsumerVersion2 {
    private int i = 0;
    private final Object LOCK = new Object();
    /**
     * 是否已經消費
     */
    private volatile boolean isProduced = false;

    public void produce() {
        synchronized (LOCK) {
            if (isProduced) {
                try {
                    System.out.println(Thread.currentThread().getName() + "wait了");
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                i++;
                System.out.println(Thread.currentThread().getName() + "生產了->" + i);
                LOCK.notify();
                System.out.println(Thread.currentThread().getName() + "notify了" + i);
                isProduced = true;
            }
        }
    }

    public void consume() {
        synchronized (LOCK) {
            if (isProduced) {
                System.out.println(Thread.currentThread().getName() + "消費了->" + i);
                LOCK.notify();
                System.out.println(Thread.currentThread().getName() + "notify了");
                isProduced = false;
            } else {
                try {
                    System.out.println(Thread.currentThread().getName() + "wait了");
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        ProduceConsumerVersion2 pc = new ProduceConsumerVersion2();

        // 多個生產者、消費者執行緒執行時,會產生一些問題
        Stream.of("P1", "P2").forEach(n ->
                new Thread(() -> {
                    while (true) {
                        pc.produce();
                    }
                }, n).start()
        );
        Stream.of("C1", "C2").forEach(n ->
                new Thread(() -> {
                    while (true) {
                        pc.consume();
                    }
                }, n).start()
        );
    }
}

執行效果如下:

可以看到P1,C1,P2,C2都wait了,然後程式就假死了,這是什麼原因呢?

這裡我就不一步一步分析了,這裡只需要明白一個道理即可,當有多個wait的時候,notify是隨機的。這個我們後續再探討,先吃個飯~