Wait()/ notify()同步

我正在尝试检查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"); 将被打印。 但它似乎只有在完成run()时才会打印出来。

对象监视器锁需要执行相同锁的单个引用…

在您的示例中,您正在waiting Thread的实例,但使用Runnable notify 。 相反,您应该使用单个公共锁定对象…例如

 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"); } } 

回答更新的代码:

来自Thread.sleep() javadoc:

导致当前正在执行的线程hibernate(暂时停止执行)指定的毫秒数,具体取决于系统计时器和调度程序的精度和准确性。 该线程不会失去任何监视器的所有权

如果在同步块内部调用Thread.sleep,则其他线程将无法进入同步块。 在同步块中,您永远不应该耗费时间来避免这种情况。

注意(正如其他人指出的那样)你必须在两个线程中使用相同的对象来锁定/同步。

如果您希望在调用notify后立即继续主线程,则必须暂时放弃锁定。 否则只有在辅助线程离开synchronized块后才会调用wait 。 在长时间运行的计算中保持锁定永远不是一个好主意!

实现的一种方法是在锁而不是sleep上使用wait(int) ,因为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或软件事务内存 ,它可以很容易地组成,并且可以免除大多数并发问题。