它有什么问题?
What is wrong with it?
我看过很多 wait
和 notify
的例子,但我还是有问题。
public class Main(){
public static void main(String args[]) throws Exception {
MyThread s = new MyThread();
s.start();
}
}
class MyThread extends Thread {
public void run() {
k();
}
public synchronized void k() {
System.out.println("before wait");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("do something after wait");
}
public synchronized void m() {
for (int i=0;i<6;i++)
System.out.println(i);
notify();
}
}
程序 运行 时我得到的唯一输出是:"before wait"
.
问题是,你没有调用你的 m
方法,所以 notify
永远不会被调用,所以你的线程永远休眠。您可以在开始后使用 s.m()
:
在 main
中调用它
MyThread s = new MyThread();
s.start();
s.m();
也许你应该在调用 m
方法之前 sleep
一段时间,因为它可以 运行 比线程中的 k
更快:
s.start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// nothing to do
}
s.m();
与问题没有密切关系,但是 main
中的 throws
声明不是很可取,即使是生成的 printStackTrace
也比将异常丢弃更好。
您在 main
中创建的线程调用 MyThread#k()
进入等待状态。那时,该线程将不会做任何其他事情,直到它被唤醒或中断。但是在您的代码中唯一可能被唤醒的地方是 MyThread#m()
中的 notify
。由于您的程序中没有任何内容调用该方法,因此线程永远不会被唤醒。
您可能想要在主程序中的 s.start()
之后添加对 s.m()
的调用。这样您的主线程将执行唤醒您的线程所需的 notify
。
不幸的是,这不太可能奏效。问题是 s.start()
导致您创建的线程准备好 运行,但不一定立即 运行。您对 s.m()
的调用很可能会在创建的线程执行任何操作之前完成。然后你仍然会得到与之前完全相同的结果,只是你会看到在 before wait
之前打印出整数 0..6。 notify
将不执行任何操作,因为子线程尚未执行其 wait
。 (顺便说一下,因为 MyThread#k()
和 MyThread#m()
都是同步的,所以增加 MyThread#m()
中的循环限制不会改变任何事情......子线程将无法输入 MyThread#k()
而 MyThread#m()
是 运行ning。您可以通过将 notify
放在同步块中而不是使所有 MyThread#m()
同步来改进它。)
您可以尝试通过在主程序中的 s.m()
之前添加 Thread.sleep(1000)
来解决这个问题。这几乎肯定会起作用,因为您的主线程将让出执行,让您的 JVM 有机会安排子线程进行一些有用的工作。当主线程从睡眠中醒来并执行它的 s.m()
调用时,子线程可能已经执行了它的 wait
然后你会看到你的 do something after wait
消息。
但这仍然很糟糕,因为它仍然取决于您实际上无法控制的安排事件。仍然保证 wait
会在 notify
之前发生。
这就是为什么在使用 wait/notify 时,您通常应该安排某种可靠的测试,以确定您等待完成的事情是否真的发生了。这应该是一个条件,一旦它变为真,将至少在随后执行测试之前保持真。然后你典型的等待循环看起来像这样:
while (!isDone()) {
synchronized(monitorObject) {
try {
monitorObject.wait();
} catch (InterruptedException e) {
}
}
}
将整个事情放在一个循环中可以解决过早醒来的问题,例如由于 InterruptedException。
如果在执行此代码时所需的工作已经发生,则不会发生 wait
,并且执行该工作的代码执行的 notify
是空操作。否则,此代码将等待,完成工作的代码最终将执行 notify
以根据需要唤醒此代码。当然,至关重要的是,在执行 notify
时,等待条件(上面的 isDone()
)为真并且至少在测试之前保持为真。
这是您的代码的更正版本,其中包含一个适当的等待循环。如果注释掉 Thread.sleep()
调用,您可能看不到 waiting
消息,因为工作会在等待循环开始之前完成。包括睡眠在内,您可能会看到 waiting
消息。但无论哪种方式,程序都会正常运行。
public static void main(String[] argv) throws Exception {
MyThread s = new MyThread();
s.start();
Thread.sleep(1000);
s.m();
}
class MyThread extends Thread {
@Override
public void run() {
k();
}
private boolean done = false;
public void k() {
System.out.println("before wait");
while (!done) {
System.out.println("waiting");
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
System.out.println("do something after wait");
}
public void m() {
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
synchronized (this) {
done = true;
notify();
}
}
}
我看过很多 wait
和 notify
的例子,但我还是有问题。
public class Main(){
public static void main(String args[]) throws Exception {
MyThread s = new MyThread();
s.start();
}
}
class MyThread extends Thread {
public void run() {
k();
}
public synchronized void k() {
System.out.println("before wait");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("do something after wait");
}
public synchronized void m() {
for (int i=0;i<6;i++)
System.out.println(i);
notify();
}
}
程序 运行 时我得到的唯一输出是:"before wait"
.
问题是,你没有调用你的 m
方法,所以 notify
永远不会被调用,所以你的线程永远休眠。您可以在开始后使用 s.m()
:
main
中调用它
MyThread s = new MyThread();
s.start();
s.m();
也许你应该在调用 m
方法之前 sleep
一段时间,因为它可以 运行 比线程中的 k
更快:
s.start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// nothing to do
}
s.m();
与问题没有密切关系,但是 main
中的 throws
声明不是很可取,即使是生成的 printStackTrace
也比将异常丢弃更好。
您在 main
中创建的线程调用 MyThread#k()
进入等待状态。那时,该线程将不会做任何其他事情,直到它被唤醒或中断。但是在您的代码中唯一可能被唤醒的地方是 MyThread#m()
中的 notify
。由于您的程序中没有任何内容调用该方法,因此线程永远不会被唤醒。
您可能想要在主程序中的 s.start()
之后添加对 s.m()
的调用。这样您的主线程将执行唤醒您的线程所需的 notify
。
不幸的是,这不太可能奏效。问题是 s.start()
导致您创建的线程准备好 运行,但不一定立即 运行。您对 s.m()
的调用很可能会在创建的线程执行任何操作之前完成。然后你仍然会得到与之前完全相同的结果,只是你会看到在 before wait
之前打印出整数 0..6。 notify
将不执行任何操作,因为子线程尚未执行其 wait
。 (顺便说一下,因为 MyThread#k()
和 MyThread#m()
都是同步的,所以增加 MyThread#m()
中的循环限制不会改变任何事情......子线程将无法输入 MyThread#k()
而 MyThread#m()
是 运行ning。您可以通过将 notify
放在同步块中而不是使所有 MyThread#m()
同步来改进它。)
您可以尝试通过在主程序中的 s.m()
之前添加 Thread.sleep(1000)
来解决这个问题。这几乎肯定会起作用,因为您的主线程将让出执行,让您的 JVM 有机会安排子线程进行一些有用的工作。当主线程从睡眠中醒来并执行它的 s.m()
调用时,子线程可能已经执行了它的 wait
然后你会看到你的 do something after wait
消息。
但这仍然很糟糕,因为它仍然取决于您实际上无法控制的安排事件。仍然保证 wait
会在 notify
之前发生。
这就是为什么在使用 wait/notify 时,您通常应该安排某种可靠的测试,以确定您等待完成的事情是否真的发生了。这应该是一个条件,一旦它变为真,将至少在随后执行测试之前保持真。然后你典型的等待循环看起来像这样:
while (!isDone()) {
synchronized(monitorObject) {
try {
monitorObject.wait();
} catch (InterruptedException e) {
}
}
}
将整个事情放在一个循环中可以解决过早醒来的问题,例如由于 InterruptedException。
如果在执行此代码时所需的工作已经发生,则不会发生 wait
,并且执行该工作的代码执行的 notify
是空操作。否则,此代码将等待,完成工作的代码最终将执行 notify
以根据需要唤醒此代码。当然,至关重要的是,在执行 notify
时,等待条件(上面的 isDone()
)为真并且至少在测试之前保持为真。
这是您的代码的更正版本,其中包含一个适当的等待循环。如果注释掉 Thread.sleep()
调用,您可能看不到 waiting
消息,因为工作会在等待循环开始之前完成。包括睡眠在内,您可能会看到 waiting
消息。但无论哪种方式,程序都会正常运行。
public static void main(String[] argv) throws Exception {
MyThread s = new MyThread();
s.start();
Thread.sleep(1000);
s.m();
}
class MyThread extends Thread {
@Override
public void run() {
k();
}
private boolean done = false;
public void k() {
System.out.println("before wait");
while (!done) {
System.out.println("waiting");
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
System.out.println("do something after wait");
}
public void m() {
for (int i = 0; i < 6; i++) {
System.out.println(i);
}
synchronized (this) {
done = true;
notify();
}
}
}