只有两个线程用于调用produce和consume?

vxqlmq5t  于 2021-07-03  发布在  Java
关注(0)|答案(3)|浏览(360)

下面是关于运行生产者和消费者的线程的代码。

public class PC1 {
    public static final int limit = 3;

    public static void main(String[] args) {
        List bag = new ArrayList();
        Producer p = new Producer(bag);
        Consumer c = new Consumer(bag);
        Thread t1 = new Thread(p, "t1");
        Thread t2 = new Thread(c, "t2");
        Thread t3 = new Thread(p, "t3");
        Thread t4 = new Thread(c, "t4");
        Thread t5 = new Thread(p, "t5");
        Thread t6 = new Thread(c, "t6");
        t2.start();
        t4.start();
        t6.start();
        t1.start();
        t3.start();
        t5.start();
    }
}

class Producer implements Runnable {
    private List bag;

    public Producer(List bag) {
        this.bag = bag;
    }

    @Override
    public void run() {
        synchronized (bag) {
            while (true) {
                while (bag.size() >= PC1.limit) {
                    bag.notify();
                    System.out.println(Thread.currentThread().getName() + "@@@@@@@@@@@");
                    try {
                        bag.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int curr = bag.size();

                bag.add(++curr);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " produce " + curr);
            }
        }
    }

}

class Consumer implements Runnable {
    private List bag;

    public Consumer(List bag) {
        this.bag = bag;
    }

    @Override
    public void run() {
        synchronized (bag) {
            while (true) {
                while (bag.size() <= 0) {
                    bag.notify();
                    System.out.println(Thread.currentThread().getName() + "!!!!!!!!!!!");
                    try {
                        bag.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int curr = bag.size();

                bag.remove(curr - 1);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " consume " + (bag.size() + 1));
            }
        }
    }

}

t2!!!!!!!!!!!
t3生产1
t3生产2
t3生产3
t3级@@@@@@@@@@@
t1级@@@@@@@@@@@
t6消耗3
t6消耗2
t6消耗1
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
.
.
……一直这样t6和t4交替
以上是我控制台上的结果see:t2,t3,t1在开始时获得执行机会,然后t6和t4交替,其他线程将永远不会获得执行机会。
让我来解释它的过程。
首先,t2获得消费特权,bag.size=0,等等
然后,t3得到特权产生,3次后,包满,通知t2使其进入竞争状态,自行等待
然后,t1获得产品特权,当包满时,通知t3使其进入竞争状态,自行等待
然后,t6获得特权消费,3次后,包为空,通知t3使其进入竞争状态,自行等待
然后,t4获得消费特权,当行李为空时,通知t6使其进入竞争状态,自行等待
.
.
.
在t4等待之前,其他5个线程都处于竞争状态,但是结果显示只有t4和t6交替,其他线程永远不会有机会执行。为什么会发生这种情况?
另一个问题是,如果我将notify修改为notifyall,那么所有6个线程都有机会执行。根据我的理解,如果多个线程都处于竞争状态,那么它们都应该有机会执行。

shstlldc

shstlldc1#

斯蒂芬的回答很好,但公平并不是唯一的问题,你对等待的工作原理有太多不准确的假设,把它们一一列举出来会让人厌烦。等待的方法很难理解。
詹姆斯的回答很有道理。你不能指望任何特定的线程得到通知。对读者和作者使用单独的条件是避免所有人都知道的一个好主意。
总的来说,访问数据结构的线程不应该进行同步,数据结构应该保护自己的状态(使用同步或可重入锁定或其他方式)。重新组织这样做,程序将变得更容易编写。此外,您还可以阅读oracle教程,以澄清您对wait/notify如何工作的误解。

6ie5vjzr

6ie5vjzr2#

通知工作正常;i、 e.根据其规定。问题是java不能保证wait/notify/notifyall的公平调度。这意味着一些线程可能比其他线程得到更多的工作。
事实上,对于一个正常的程序来说,这并不重要:这根本不是一个问题。例如,在一个普通的多线程生产者/消费者应用程序中,哪个消费者线程处理生产者产生的东西并不重要。重要的是它们的处理效率。实际上,使用不公平的线程调度而不是公平的线程调度会有性能优势。一个原因是可以减少线程上下文切换的数量。
那么你将如何实现你的目标呢?
首先,不要使用wait/notify。如果您阅读这些方法的javadocs,您将看到没有公平性的保证。
获得公平的一种方法是使用 ReentrantLock 示例化为 fair == true ; 有关详细信息,请参阅javadoc。

ukxgm1gy

ukxgm1gy3#

如果你要有不止一个生产者和一个消费者,那么你必须使用 notifyAll() ; 或者您必须完全放弃wait()/notify(),切换到 ReentrantLock 相反,使用两个 Conditions .
使用的问题 notify() 当存在多个生产者或消费者时,在某些情况下,生产者有可能唤醒另一生产者,或者消费者有可能唤醒另一消费者。一旦发生这种情况,周期就被打破了,您的程序将无法再取得任何进展。有点像死锁:每个线程都在等待其他线程执行某些操作。
解决这个问题的妙方是把 Condition 变量:一个是生产者唤醒消费者,一个是消费者唤醒生产者。解决问题的钝器方法是使用 notifyAll() 把所有人都叫醒。

相关问题