多线程Java等待/通知未唤醒线程
我正在做一个小练习来适应等待/通知。 我试图做的是简单地启动一个线程,然后用wait将其休眠,然后用notify将其唤醒,多次
我的代码是:
public class Simple{
static final Thread mainThread = Thread.currentThread();
public static void main(String[] args) throws InterruptedException {
PrintThread printer = new PrintThread(0);
printer.start();
synchronized (mainThread){
System.out.println("main sleeping while waiting for printer to be started");
mainThread.wait();
System.out.println("main woke up");
for (int i = 0; i < 1000; i++) {
synchronized (printer){
System.out.println("added num "+i);
printer.numToPrint = i;
System.out.println("main waking up printer");
printer.notifyAll();
System.out.println("main sleeping");
mainThread.wait();
System.out.println("main woke up");
}
}
}
}
}
class PrintThread extends Thread{
public int numToPrint = -1;
public PrintThread(int numToPrint){
this.numToPrint = numToPrint;
}
@Override
public synchronized void run() {
System.out.println("printer started");
while (true){
try {
synchronized (Simple.mainThread){
System.out.println("printer waking up main");
Simple.mainThread.notifyAll();
}
System.out.println("printer sleeping");
wait();
System.out.println("printer woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("printing num "+numToPrint);
}
}
}
我希望事情会像这样发展
main sleeping while waiting for printer to be started
printer started
printer waking up main
printer sleeping
main woke up
added num 0
main waking up printer
main sleeping
printer woke up
printing num 0
printer waking up main
printer sleeping
main woke up
added num 1
...
而是:
main sleeping while waiting for printer to be started
printer started
printer waking up main
printer sleeping
main woke up
added num 0
main waking up printer
main sleeping
所以。。。好像notify没有唤醒打印机线程
这不应该是一个死锁,因为通过等待,我可以解除所有的锁定,所以主不应该对打印机有任何锁定,打印机应该能够唤醒并打印
我做错了什么
# 1 楼答案
很可能是在再次调用Print calls wait()之前调用了notifyAll()调用。问题在于你依赖于等待和通知,所有的呼叫都是按照你希望的顺序进行的。这是两个不同的执行线程,所以当然这不是保证,因此你得到了你所得到的
实现这一点的更好方法是创建一个公共的第三共享对象,这两个线程都可以获得锁定。这将使两个线程在等待访问该对象时同步
此外,您还应该阅读Java文档中的线程。等等,通知,然后通知所有人。如果/当您这样做时,您将看到您永远不应该在线程上调用这些方法,因为它们用于执行线程。join(不仅如此,我的“声名远扬”是因为我相信几年前我的错误请求记录了这一点,而这并不是在JavaDoc中,这导致它被添加到JavaDoc中。可能是其他人,但它发生在我请求之后:)
# 2 楼答案
属性:调用wait()释放锁(它正在监视的锁)并进入等待状态。它在同一对象上等待notify()或notifyAll()。一旦notify()或notifyAll()在CPU中得到调度,它将在恢复之前再次获取锁
当您在main方法中第一次执行“synchronized(mainThread)”时,它基本上锁定了“mainThread”类对象。当主线程。调用wait(),mainThread进入等待状态(等待有人在mainThread类对象上调用notify或notifyAll)
此时,PrintThread可能会获得CPU。这是“synchronized(Simple.mainThread)”被调度并锁定“Simple.mainThread”并通知所有等待“Simple.mainThread”的线程的时候。在这个块完成后,PrintThread释放“Simple.mainThread”锁
此时,主线程将再次尝试获取“主线程”上的锁,然后从调用wait的位置恢复。由于此时未获取“mainThread”锁,因此主线程获取锁并打印“main wakeup”
现在,这里遇到了for循环。 记住:这里已经获得了对“mainThread”类对象的锁定
现在在for循环内部,它获得了对“printer”对象的锁定。执行一些计算,并调用“printer.notifyAll()”,所有等待“printer”对象的线程都将收到通知
**这里需要记住的一点是:由于代码光标仍在“synchronized(printer)”内,“printer”对象的锁定尚未解除。**
向前看,会打印“main sleeping”,然后调用“mainThread.wait()”。这将尝试获取已获取的“mainThread”上的锁(如上所述,其中“记住:”在块中),并被卡住,因为此后没有线程在“mainThread”上进行通知,“synchronized(printer)”块永远不会结束,即即使在调用NotifyAll()之后,“printer”对象上的锁也永远不会解除
尝试在开始时在main方法中添加以下代码,以测试上述场景
解决方案: 在“printer.notifyAll()”之后关闭“synchronized(printer)”块,以便在通知之后和获取“main thread”之前释放“printer”锁