我是多线程的新手,正在尝试一个家庭有母亲(作为制作人),儿子,女儿和丈夫[作为消费者]线程的场景。我想了解等待和通知方法如何在这里帮助
我的课程如下。
package com.test.All.Threads;
public enum MotherAsProducer {
INSTANCE;
/*
*
*
* son Give request to prepare chapati to mother
* mother accepts it and start preparing , son/husband/daughter should wait by that time.
* mother notifies son/daughtor/husband that chapati is ready start consuming
* */
public synchronized void takeOrderAndMakeChapati(){
try {
System.out.println("Request got from "+Thread.currentThread().getName());
getStatusOfChapati();
wait();
System.out.println(Thread.currentThread().getName()+" ate chapati");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//lock re-entrance
public synchronized void getStatusOfChapati(){
try {
Thread.sleep(1200);
System.out.println("Chapati is prepared for "+Thread.currentThread().getName());
notifyAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static MotherAsProducer getMotherInstance(){
return MotherAsProducer.INSTANCE;
}
}
package com.test.All.Threads;
public class SonAsConsumer implements Runnable{
public void run(){
MotherAsProducer.getMotherInstance().takeOrderAndMakeChapati();
}
}
package com.test.All.Threads;
public class DaughterAsConsumer implements Runnable {
public void run(){
MotherAsProducer.getMotherInstance().takeOrderAndMakeChapati();
}
}
package com.test.All.Threads;
public class HusbandAsConsumer implements Runnable {
public void run(){
MotherAsProducer.getMotherInstance().takeOrderAndMakeChapati();
}
}
package com.test.All.Threads;
public class Home {
public static void main(String args[]){
SonAsConsumer sac = new SonAsConsumer();
DaughterAsConsumer dac = new DaughterAsConsumer();
HusbandAsConsumer hac = new HusbandAsConsumer();
Thread tsac = new Thread(sac);
tsac.setName("Son");
Thread tdac = new Thread(dac);
tdac.setName("Daughter");
Thread thac = new Thread(hac);
thac.setName("Husband");
tsac.start();
tdac.start();
thac.start();
}
}
我的输出是不同的,每次都是按照线程的性质预期但是丈夫,女儿或儿子中的一个人没有完成。 我的输出的一个实例如下。
Order she got from Daughter
Chapati is prepared for Daughter
Order she got from Son
Chapati is prepared for Son
Order she got from Husband
Chapati is prepared for Husband
Son ate chapati
Daughter ate chapati
我的理解是,当儿子,女儿和丈夫将开始执行时,其中一个将按下synchronized方法并执行wait()并将持有锁,再次从该同步方法调用另一个同步方法,该方法将包含notify和锁将被释放,另一个线程将尝试从被阻塞的池中获取锁,并将以相同的方式执行。这里有两个线程按预期运行,但最后一个不是。 请帮助。
答案 0 :(得分:1)
简单地说,看起来最后一个等待的线程永远不会得到通知。对您的调用进行排序,您可以让每个线程获得锁定,通知所有等待的线程,然后等待。因此,等待等待的最后一个线程将永远不会有任何人通知他们需要退出。
也就是说,如果线程A最初获得锁定,那么它将执行println和sleep,然后执行println,然后通知所有等待线程(没有),然后成为等待线程。
然后,让我们说线程B获得锁定。它将执行println和sleep,然后它将通知所有(它将通知"通知"线程A),然后它将等待。
现在,线程C或线程A将获得锁定。如果线程A得到它,它将简单地落入并完成" ate"信息。然后,线程C可以获得锁定,它最终会通知,唤醒B,一旦C"等待"就可以吃掉。现在,没有任何线程可以通知,以便C完成。
这有道理吗?我误读了什么吗?
要验证我建议的错误,只需添加更多主题即可。你应该总是有最后一个打印" Chapati准备好..."永远不会吃它。
从根本上说,我认为混淆是"母亲"实际上并没有做任何工作。你可能想要的是拥有"母亲"是一个拥有自己的工作日志的线程。因此,当其他一个线程给她的工作时,你设置一个变量然后通知母亲并等待兄弟姐妹。然后母亲将醒来并完成工作并通知当前线程等待。
明白我的意思?比喻说,这个程序中有4个人。但是,你只有3个线程。
答案 1 :(得分:0)
更改枚举类MotherAsProducer中的方法,如下所示:不必要的wait()方法导致了该问题。由于该方法是同步的,因此在进入方法之前将阻止所有其他线程,直到从锁定保持线程获取通知为止。
public synchronized void takeOrderAndMakeChapati(){
System.out.println("Request got from " + Thread.currentThread().getName());
getStatusOfChapati();
// wait(); - THIS WAIT IS CAUSING THE PROBLEM
System.out.println(Thread.currentThread().getName() + " ate chapati");
}
答案 2 :(得分:0)
从wait
和notifyAll
移除takeOrderAndMakeChapati
和getStatusOfChapati
来电。您将获得预期的结果。
正如Josh所提到的,其中一个主题(最后一个)仍在wait
进行一些外部通知,没有人可以通知。您的代码仍在后台运行。只需致电wait(5000)
,您就会发现它正在发生。
方法takeOrderAndMakeChapati
和getStatusOfChapati
都是同步的,因此同步不是问题。
通常线程wait
用于某些外部依赖项或条件,其中一些其他线程在满足该条件时通知等待的线程。
答案 3 :(得分:0)
当我开始使用多线程时,我还尝试理解wait
和notify
。但是当我学会使用Semaphore
时,我再也没有回头。希望下面的例子能让您深入了解使用信号量的好处。在java.util.concurrent
包中还有很多有用的东西可以提供很大的帮助。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class EatChapati {
static int CHAPATI_PREPARE_TIME_MS = 100;
static long RUN_TIME_MS = 2000;
static long SHUTDOWN_TIME_MS = 500;
static int CHAPATI_CONSUMERS = 5;
static volatile boolean stop;
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < CHAPATI_CONSUMERS; i++) {
executor.execute(new ChapatiConsumer(i + 1));
}
try { Thread.sleep(RUN_TIME_MS); } catch (Exception ignored) {}
stop = true;
executor.shutdownNow();
try { executor.awaitTermination(SHUTDOWN_TIME_MS, TimeUnit.MILLISECONDS); } catch (Exception ignored) {}
}
// 1 producer making chapati's
// 'true' for a fair semaphore: longest waiting consumer gets served
static Semaphore chapatiTurn = new Semaphore(1, true);
static AtomicInteger chapatiCount = new AtomicInteger();
static int getChapati(int consumerNumber) {
int chapatiNumber = 0;
boolean haveTurn = false;
try {
chapatiTurn.acquire();
// start of 'synchronized' block
haveTurn = true;
Thread.sleep(CHAPATI_PREPARE_TIME_MS);
chapatiNumber = chapatiCount.incrementAndGet();
System.out.println("Chapati " + chapatiNumber + " prepared for consumer " + consumerNumber);
} catch (Exception e) {
// Triggered by executor.shutdownNow
stop = true;
} finally {
if (haveTurn) {
chapatiTurn.release();
// end of 'synchronized' block
}
}
return chapatiNumber;
}
static class ChapatiConsumer implements Runnable {
int number;
ChapatiConsumer(int number) {
this.number = number;
}
public void run() {
int chapatisConsumed = 0;
while (!stop) {
if (getChapati(number) > 0) {
chapatisConsumed++;
}
}
System.out.println("Consumer " + number + " stopped after consuming " + chapatisConsumed + " chapatis.");
}
}
}