下面是关于运行生产者和消费者的线程的代码。
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个线程都有机会执行。根据我的理解,如果多个线程都处于竞争状态,那么它们都应该有机会执行。
3条答案
按热度按时间shstlldc1#
斯蒂芬的回答很好,但公平并不是唯一的问题,你对等待的工作原理有太多不准确的假设,把它们一一列举出来会让人厌烦。等待的方法很难理解。
詹姆斯的回答很有道理。你不能指望任何特定的线程得到通知。对读者和作者使用单独的条件是避免所有人都知道的一个好主意。
总的来说,访问数据结构的线程不应该进行同步,数据结构应该保护自己的状态(使用同步或可重入锁定或其他方式)。重新组织这样做,程序将变得更容易编写。此外,您还可以阅读oracle教程,以澄清您对wait/notify如何工作的误解。
6ie5vjzr2#
通知工作正常;i、 e.根据其规定。问题是java不能保证wait/notify/notifyall的公平调度。这意味着一些线程可能比其他线程得到更多的工作。
事实上,对于一个正常的程序来说,这并不重要:这根本不是一个问题。例如,在一个普通的多线程生产者/消费者应用程序中,哪个消费者线程处理生产者产生的东西并不重要。重要的是它们的处理效率。实际上,使用不公平的线程调度而不是公平的线程调度会有性能优势。一个原因是可以减少线程上下文切换的数量。
那么你将如何实现你的目标呢?
首先,不要使用wait/notify。如果您阅读这些方法的javadocs,您将看到没有公平性的保证。
获得公平的一种方法是使用
ReentrantLock
示例化为fair == true
; 有关详细信息,请参阅javadoc。ukxgm1gy3#
如果你要有不止一个生产者和一个消费者,那么你必须使用
notifyAll()
; 或者您必须完全放弃wait()/notify(),切换到ReentrantLock
相反,使用两个Conditions
.使用的问题
notify()
当存在多个生产者或消费者时,在某些情况下,生产者有可能唤醒另一生产者,或者消费者有可能唤醒另一消费者。一旦发生这种情况,周期就被打破了,您的程序将无法再取得任何进展。有点像死锁:每个线程都在等待其他线程执行某些操作。解决这个问题的妙方是把
Condition
变量:一个是生产者唤醒消费者,一个是消费者唤醒生产者。解决问题的钝器方法是使用notifyAll()
把所有人都叫醒。