我正在尝试检查java中的wait / notify如何工作。
代码:
public class Tester {
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
synchronized (t) {
try {
System.out.println("wating for t to complete");
t.wait();
System.out.println("wait over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable implements Runnable {
public void run() {
System.out.println("entering run method");
synchronized (this) {
System.out.println("entering syncronised block");
notify();
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("leaving syncronized block");
}
System.out.println("leaving run method");
}
}
输出已退回
wating for t to complete
entering run method
entering syncronised block
//sleep called
leaving syncronized block
leaving run method
wait over
我期待当执行notify()时,等待将结束& System.out.println("wait over");
将被打印出来。但它似乎只有在t
完成run()
时才会打印出来。
答案 0 :(得分:11)
对象监视器锁需要执行相同锁的单个引用...
在您的示例中,您waiting
的实例Thread
,但使用notify
中的Runnable
。相反,您应该使用单个公共锁定对象...例如
public class Tester {
public static final Object LOCK = new Object();
public static void main(String[] args) {
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
synchronized (LOCK) {
try {
System.out.println("wating for t to complete");
LOCK.wait();
System.out.println("wait over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class MyRunnable implements Runnable {
public void run() {
System.out.println("entering run method");
synchronized (LOCK) {
System.out.println("entering syncronised block");
LOCK.notify();
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("leaving syncronized block");
}
System.out.println("leaving run method");
}
}
}
<强>输出... 强>
wating for t to complete
entering run method
entering syncronised block
leaving syncronized block
wait over
leaving run method
wait over
和leaving run method
可能会根据线程安排更改位置。
您可以尝试将synchronized
块放在一边。这将释放监视器锁定,允许wait
部分继续运行(因为它在释放锁之前无法启动)
public static class MyRunnable implements Runnable {
public void run() {
System.out.println("entering run method");
synchronized (LOCK) {
System.out.println("entering syncronised block");
LOCK.notify();
System.out.println("leaving syncronized block");
}
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("leaving run method");
}
}
答案 1 :(得分:5)
回答更新的代码:
来自Thread.sleep() javadoc:
导致当前正在执行的线程休眠(暂时停止执行) 指定的毫秒数,取决于系统计时器的精度和准确性 和调度程序。 该主题不会失去任何监视器的所有权。
如果在同步块内部调用Thread.sleep,则其他线程将无法进入同步块。在同步块中,您永远不应该耗费时间来避免这种情况。
答案 2 :(得分:1)
注意(正如其他人指出的那样)你必须在两个线程中使用相同的对象来锁定/同步。
如果您想在调用notify
后立即继续主线程,则必须暂时放弃锁定。否则,只有在辅助线程离开wait
块后才会调用synchronized
。在长时间运行的计算中保持锁定永远不是一个好主意!
如何实现的一种方法是在锁上使用wait(int)
而不是sleep
,因为wait
会暂时释放同步锁:
public class Tester {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
synchronized (lock) {
try {
System.out.println("wating for t to complete");
lock.wait();
System.out.println("wait over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyRunnable implements Runnable {
public void run() {
System.out.println("entering run method");
synchronized (lock) {
System.out.println("entering syncronised block");
lock.notify();
try {
lock.wait(1000); // relinquish the lock temporarily
} catch (InterruptedException ex) {
System.out.println("got interrupted");
}
System.out.println("leaving syncronized block");
}
System.out.println("leaving run method");
}
}
}
然而,使用这些低级原语可能非常容易出错,我不鼓励使用它们。相反,我建议你使用Java的高级原语。例如,您可以使用CountDownLatch
让一个线程等待,直到其他线程倒计数为零:
import java.util.concurrent.*;
public class TesterC {
private static final CountDownLatch latch = new CountDownLatch(1);
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
System.out.println("wating for t to complete");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("wait over");
}
static class MyRunnable implements Runnable {
public void run() {
System.out.println("entering run method");
try {
latch.countDown();
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.out.println("got interrupted");
}
System.out.println("leaving run method");
}
}
}
在这里你不必同步任何东西,闩锁为你做了一切。您可以使用许多其他原语 - 信号量,交换器,线程安全队列等。资源管理器java.util.concurrent
包。
或许更好的解决方案是使用更高级别的API,例如Akka提供的。在那里,您可以使用Actors或Software transactional memory,它可以轻松编写,并且可以免除大多数并发问题。