我正在尝试做一些小练习以习惯于等待/通知。我正在尝试做的只是简单地启动一个线程,然后等待它使其进入睡眠状态,并通过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不会唤醒打印机线程?
这不应该是死锁,因为通过等待我会释放我拥有的所有锁,因此main不应在printer上有任何锁,并且printer应该能够唤醒并打印。
我在做什么错?
最有可能在Print再次调用wait()之前调用notifyAll()调用。问题在于您是否完全按照您希望的顺序来执行wait和notifyAll调用。这是两个不同的执行线程,因此当然不能保证,因此您正在获得所得到的。
实现此目的的一种更好的方法是创建一个公共的第3个共享对象,两个线程都可以获取锁定。这将使两个线程在等待访问该对象时进行同步。
[此外,您应该阅读Thread.wait的Javadocs,并通知和notifyAll。如果/当您这样做时,您将看到永远不要在Threads上调用这些方法,因为它们在执行thread.join中使用(不仅如此,我的“成名声”还是我几年前提出的错误请求)当它不在JavaDoc中导致将其添加到Javadoc中时对此进行记录。本来可以是其他人,但是在我要它之后就发生了:))
Property:调用wait()释放锁定(它正在监视该锁定)并进入等待状态。它在同一对象上等待notify()或notifyAll()。一旦notify()或notifyAll()并在CPU中进行了调度,它会在恢复之前再次获取该锁。
当您首先在main方法中执行“ synchronized(mainThread)”时,它基本上锁定了“ mainThread”类对象。当调用mainThread.wait()时,mainThread进入等待状态(等待某人调用mainThread类对象上的notify或notifyAll)。
这次,PrintThread可能会占用CPU。这是“ synchronized(Simple.mainThread)”被安排并锁定“ Simple.mainThread”并通知所有等待“ Simple.mainThread”的线程时的时间。在此块完成之后,PrintThread释放了“ Simple.mainThread”锁。
此时主线程将在尝试从何处恢复之前尝试再次获取“ mainThread”上的锁。由于此时尚未获取“ mainThread”锁,因此主线程将获得该锁并打印“ main wake up”。
现在,在这里遇到for循环。记住:此处已获得对“ mainThread”类对象的锁定。
现在在for循环中,它获得了对“打印机”对象的锁定。执行一些计算并调用“ printer.notifyAll()”,将通知所有在“ printer”对象上等待的线程。
**这里要记住的要点是:由于代码光标仍位于“同步(打印机)”内部,因此尚未释放对“打印机”对象的锁定。 **
向前移动,打印“ main sleep”,然后调用“ mainThread.wait()”。这试图获取已获取的“ mainThread”上的锁(在块中的“ Remember:”中已提及),并且由于以后没有线程在“ mainThread”上进行通知而被卡住,并且“ synchronized(printer)”块永远不会结束,即锁定即使调用NotifyAll(),也永远不会释放“打印机”对象。
尝试从头开始在主要方法中添加以下代码,以测试上述情况。
synchronized (mainThread) {
synchronized (printer){
System.out.println("Before");
mainThread.wait();
System.out.println("After");
}
SOLUTION:在“ printer.notifyAll()”之后关闭“ synchronized(printer)”块,以便在通知之后并在获取“ mainThread”之前释放“ printer”锁。