Java wait/notify - 没有唤醒线程
Java wait/notify - not waking up thread
我正在尝试做一个小练习来适应 wait/notify。
我想做的是简单地启动一个线程,然后通过 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
所以...通知似乎没有唤醒打印机线程?
这不应该是死锁,因为通过等待我释放了我所有的锁,所以 main 不应该对 printer[ 有任何锁定=32=] 和 打印机 应该可以唤醒并打印。
我做错了什么?
很可能在 Print 再次调用 wait() 之前调用了 notifyAll() 调用。问题是您对 wait 和 notifyAll 调用的依赖完全按照您希望的顺序进行。这是两个不同的执行线程,所以当然这不能保证,因此你得到了你所得到的。
实现此目的的更好方法是创建一个公共的第 3 个共享对象,两个线程都可以在该对象上获取锁。这将同步两个线程,因为它们等待访问此对象。
此外,您应该阅读 Thread.wait、notify 和 notifyAll 的 Javadoc。 If/when 你这样做,你会发现你永远不应该在线程上调用这些方法,因为它们用于执行 thread.join (不仅如此,我的 "claim to fame" 是我相信这是我的几年前的 bug 请求记录这个,当时它不在 JavaDoc 中导致它被添加到 Javadoc。可能是其他人,但它发生在我要求它之后:))
属性 : 调用 wait() 释放锁(它正在监视)并进入等待状态。它在同一对象上等待 notify() 或 notifyAll()。一旦 notify() 或 notifyAll() 并在 CPU 中被调度,它会在恢复之前再次获取锁。
当您在 main 方法中首先执行 "synchronized (mainThread)" 时,它基本上锁定了 "mainThread" class 对象。当调用 mainThread.wait() 时,mainThread 进入等待状态(等待有人调用 mainThread class 对象上的 notify 或 notifyAll)。
此时 PrintThread 可能会得到 CPU。这是 "synchronized (Simple.mainThread)" 被调度并锁定 "Simple.mainThread" 并通知所有等待 "Simple.mainThread" 的线程的时候。在此块完成后,PrintThread 释放 "Simple.mainThread" 锁。
此时主线程将尝试在 "mainThread" 上再次获取锁,然后从调用 wait 的地方恢复。由于此时 "mainThread" 锁尚未获取,主线程获取锁并打印 "main woke up"。
现在,这里遇到了for循环。
记住: 这里锁定 "mainThread" class 对象已经被获取。
现在在 for 循环中,它获取 "printer" 对象上的锁。进行一些计算并调用 "printer.notifyAll()" 并通知所有等待 "printer" 对象的线程。
** 此处要记住的是:由于代码游标仍在 "synchronized (printer)" 内,因此尚未释放 "printer" 对象上的锁。 **
向前移动,打印 "main sleeping",然后调用 "mainThread.wait()"。这会尝试获取已获取的 "mainThread" 上的锁(上面提到 "Remember :" 在块中)并卡住,因为此后没有线程通知 "mainThread" 并且 "synchronized (printer)" 块永远不会结束即即使在调用 NotifyAll() 之后也不会释放对 "printer" 对象的锁定。
尝试在开始的 main 方法中添加以下代码,以测试上面的场景。
synchronized (mainThread) {
synchronized (printer){
System.out.println("Before");
mainThread.wait();
System.out.println("After");
}
解决方案:
在 "printer.notifyAll()" 之后关闭 "synchronized (printer)" 块,以便在通知之后和获取 "mainThread".
之前释放 "printer" 锁
我正在尝试做一个小练习来适应 wait/notify。 我想做的是简单地启动一个线程,然后通过 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
所以...通知似乎没有唤醒打印机线程?
这不应该是死锁,因为通过等待我释放了我所有的锁,所以 main 不应该对 printer[ 有任何锁定=32=] 和 打印机 应该可以唤醒并打印。
我做错了什么?
很可能在 Print 再次调用 wait() 之前调用了 notifyAll() 调用。问题是您对 wait 和 notifyAll 调用的依赖完全按照您希望的顺序进行。这是两个不同的执行线程,所以当然这不能保证,因此你得到了你所得到的。
实现此目的的更好方法是创建一个公共的第 3 个共享对象,两个线程都可以在该对象上获取锁。这将同步两个线程,因为它们等待访问此对象。
此外,您应该阅读 Thread.wait、notify 和 notifyAll 的 Javadoc。 If/when 你这样做,你会发现你永远不应该在线程上调用这些方法,因为它们用于执行 thread.join (不仅如此,我的 "claim to fame" 是我相信这是我的几年前的 bug 请求记录这个,当时它不在 JavaDoc 中导致它被添加到 Javadoc。可能是其他人,但它发生在我要求它之后:))
属性 : 调用 wait() 释放锁(它正在监视)并进入等待状态。它在同一对象上等待 notify() 或 notifyAll()。一旦 notify() 或 notifyAll() 并在 CPU 中被调度,它会在恢复之前再次获取锁。
当您在 main 方法中首先执行 "synchronized (mainThread)" 时,它基本上锁定了 "mainThread" class 对象。当调用 mainThread.wait() 时,mainThread 进入等待状态(等待有人调用 mainThread class 对象上的 notify 或 notifyAll)。
此时 PrintThread 可能会得到 CPU。这是 "synchronized (Simple.mainThread)" 被调度并锁定 "Simple.mainThread" 并通知所有等待 "Simple.mainThread" 的线程的时候。在此块完成后,PrintThread 释放 "Simple.mainThread" 锁。
此时主线程将尝试在 "mainThread" 上再次获取锁,然后从调用 wait 的地方恢复。由于此时 "mainThread" 锁尚未获取,主线程获取锁并打印 "main woke up"。
现在,这里遇到了for循环。 记住: 这里锁定 "mainThread" class 对象已经被获取。
现在在 for 循环中,它获取 "printer" 对象上的锁。进行一些计算并调用 "printer.notifyAll()" 并通知所有等待 "printer" 对象的线程。
** 此处要记住的是:由于代码游标仍在 "synchronized (printer)" 内,因此尚未释放 "printer" 对象上的锁。 **
向前移动,打印 "main sleeping",然后调用 "mainThread.wait()"。这会尝试获取已获取的 "mainThread" 上的锁(上面提到 "Remember :" 在块中)并卡住,因为此后没有线程通知 "mainThread" 并且 "synchronized (printer)" 块永远不会结束即即使在调用 NotifyAll() 之后也不会释放对 "printer" 对象的锁定。
尝试在开始的 main 方法中添加以下代码,以测试上面的场景。
synchronized (mainThread) {
synchronized (printer){
System.out.println("Before");
mainThread.wait();
System.out.println("After");
}
解决方案: 在 "printer.notifyAll()" 之后关闭 "synchronized (printer)" 块,以便在通知之后和获取 "mainThread".
之前释放 "printer" 锁