java - 只有 2 个线程用于调用生产和消费?

标签 java multithreading producer-consumer

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

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交替

上面是我控制台上的结果,可以看到: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个线程都有机会执行。在我的理解中,如果多线程都处于竞争状态,那么它们都应该有机会执行。

最佳答案

通知工作正常;即根据其指定方式。 “问题”在于Java不保证wait/notify/notifyAll的公平调度。这意味着某些线程可能比其他线程承担更多的工作。

事实上,对于一个正常的程序来说,这并不重要:根本不是问题。例如,在普通的多线程生产者/消费者应用程序中,哪个消费者线程处理生产者生产的东西并不重要。重要的是它们得到有效处理。事实上,使用不公平的线程调度而不是公平的线程调度可能会带来性能优势。原因之一是可以减少线程上下文切换的次数。

<小时/>

那么你将如何实现你的目标?

首先,不要使用wait/notify。如果您阅读这些方法的 javadoc,您会发现它们无法保证公平性。

获得公平性的一种方法是使用通过fair == true实例化的ReentrantLock;请参阅javadoc了解详情。

关于java - 只有 2 个线程用于调用生产和消费?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37136501/

相关文章:

java - 如何实现具有2个独立键的 map ?

java - 我如何合并红绿色和蓝色位图以获得原始位图

c++ - 惰性初始化缓存...如何使其成为线程安全的?

java - 使用 java.util.concurrent 类时我什么时候应该担心虚假唤醒

java - Spring 应用的 Permgen 空间错误

java - 如何区分从不同目录启动的相同 jar ?

java - 使用Jedis 2.8.0对吞吐量进行基准测试的Redis Cluster的可伸缩性

ruby - 使用 EventMachine 设置无限循环以生成随机数据

java - 我的 java 程序中的监视器陷入死锁

java - 在 Apache Kafka 中设置多个分区