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或软件事务内存 ,它可以很容易地组成,并且可以免除大多数并发问题。