我正在尝试理解线程是如何工作的,我写了一个简单的例子,我想创建并启动一个新线程,线程,在主线程中显示从1到1000的数字,恢复辅助线程,并在辅助线程中显示1到1000之间的数字。当我省略Thread.wait()/ Thread.notify()时,它的行为与预期一致,两个线程一次显示几个数字。当我添加这些函数时,由于某种原因,主线程的数字是第二个而不是第一个打印的。我究竟做错了什么?
public class Main {
public class ExampleThread extends Thread {
public ExampleThread() {
System.out.println("ExampleThread's name is: " + this.getName());
}
@Override
public void run() {
for(int i = 1; i < 1000; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println(i);
}
}
}
public static void main(String[] args) {
new Main().go();
}
public void go() {
Thread t = new ExampleThread();
t.start();
synchronized(t) {
try {
t.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i = 1; i < 1000; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println(i);
}
synchronized(t) {
t.notify();
}
}
}
答案 0 :(得分:11)
您误解了wait
/ notify
的工作原理。 wait
不阻止调用它的线程;它会阻止当前线程,直到在同一个对象上调用notify (所以如果你有线程A和B,而在线程A中调用了B.wait(),只要没有调用B.notify(),这将停止线程A和不线程B - 。
因此,在您的具体示例中,如果您希望首先执行主线程,则需要将wait()放在辅助线程中。像这样:
public class Main {
public class ExampleThread extends Thread {
public ExampleThread() {
System.out.println("ExampleThread's name is: " + this.getName());
}
@Override
public void run() {
synchronized (this) {
try {
wait();
} catch (InterruptedException e) {
}
}
for(int i = 1; i < 1000; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println(i);
}
}
}
public static void main(String[] args) {
new Main().go();
}
public void go() {
Thread t = new ExampleThread();
t.start();
for(int i = 1; i < 1000; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println(i);
}
synchronized(t) {
t.notify();
}
}
}
但是,即使此代码可能无法正常工作。在主线程到达之前的notify()部分的情况下,辅助线程有机会到达wait()部分(在你的情况下不太可能,但仍然可能 - 你可以观察它)如果你把Thread.sleep放在辅助线程的开头),那么辅助线程永远不会被唤醒。因此,最安全的方法与此类似:
public class Main {
public class ExampleThread extends Thread {
public ExampleThread() {
System.out.println("ExampleThread's name is: " + this.getName());
}
@Override
public void run() {
synchronized (this) {
try {
notify();
wait();
} catch (InterruptedException e) {
}
}
for(int i = 1; i < 1000; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println(i);
}
}
}
public static void main(String[] args) {
new Main().go();
}
public void go() {
Thread t = new ExampleThread();
synchronized (t) {
t.start();
try {
t.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int i = 1; i < 1000; i++) {
System.out.println(Thread.currentThread().getName());
System.out.println(i);
}
synchronized(t) {
t.notify();
}
}
}
在此示例中,输出完全是确定性的。这是发生的事情:
t
对象。t
监视器。t
主题。t
监视器,因此辅助线程无法继续并且必须等待(因为它的第一个语句是synchronized (this)
,不是,因为碰巧是 t
对象 - 所有锁定,通知和等待都可以完全与完全无关的具有相同结果的2个线程中的任何一个完成。t.wait()
部分并暂停执行,释放它同步的t
监视器。t
监视器的所有权。t.notify()
,唤醒主线程。但是主线程还不能继续,因为辅助线程仍然拥有t
监视器的所有权。t.wait()
,暂停执行并释放t
监视器。t
监视器现在可用。t
监视器的所有权,但立即释放它。t
监视器的所有权。t.notify()
,唤醒辅助线程。辅助线程暂时无法继续,因为主线程仍然保留t
监视器。t
监视器并终止。t
监视器的所有权,但立即释放它。正如你所看到的,即使是在这样一个看似简单的场景中,也会有很多事情发生。
答案 1 :(得分:2)
ExampleThread
不会wait()
或notify()
,也不会synchronized
。所以它会在没有与其他线程协调的情况下随时运行。
主线程正在等待永远不会发出的通知(此通知应由另一个线程发送)。我的猜测是当ExampleThread
死亡时,主线程被“虚假地”唤醒并完成。
应该等待另一个完成的线程必须在检查条件的循环内执行对wait()
的调用:
class ExampleThread extends Thread {
private boolean ready = false;
synchronized void ready() {
ready = true;
notifyAll();
}
@Override
public void run() {
/* Wait to for readiness to be signaled. */
synchronized (this) {
while (!ready)
try {
wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
return; /* Interruption means abort. */
}
}
/* Now do your work. */
...
然后在你的主线程中:
ExampleThread t = new ExampleThread();
t.start();
/* Do your work. */
...
/* Then signal the other thread. */
t.ready();
答案 2 :(得分:2)