2013-07-01 118 views
8

我想检查如何等待/通知在java中工作。等待()/通知()同步

代码:

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 

我所期待的通知()时执行的等待将超过& System.out.println("wait over");将得到打印。但它似乎只在t完成其run()时才被打印出来。

+5

你没有在同一个对象上同步 – MadProgrammer

+0

MyRunnable.this == r!= t –

+1

@ raul8编辑问题并粘贴到答案中使正确答案无效。最好再增加一个问题。 –

回答

9

对象监视器锁需要进行相同的锁单参考...

在你的榜样,你是waitingThread的实例,但使用notifyRunnable。相反,你应该使用一个单一的,共同的锁定对象...例如

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 overleaving run method可以改变取决于线程调度位置。

您可以尝试将​​区块的睡眠偏出。这将释放监视器锁定,允许(直到锁被释放,因为它无法启动)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"); 
     } 
    } 
+0

谢谢...但我试过你的代码..但它仍然无法正常工作。有问题的代码更新 – reiley

+0

似乎工作好吧我。你在期待什么? – MadProgrammer

+0

FYI''Thread.sleep''(两种形式)都是一种静态方法,它总是使调用线程进入休眠状态;因此''Thread.currentThread()。sleep(1000)''在语义上是多余的,可能会产生误导(例如调用t.sleep(1000)''会使调用线程进入睡眠状态,而不是t)。 – kbolino

5

回答到更新的代码:

Thread.sleep()的javadoc:

使系统定时器 和调度程序的精度和准确度符合 指定的毫秒数,使当前正在执行的线程进入休眠状态(暂时停止执行)。 线程不会丢失任何显示器的所有权

如果在同步块内部调用Thread.sleep,其他线程将无法进入同步块。您不应该在同步块中执行耗时的任务来避免这种情况。

1

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

如果您希望主线程在调用notify后立即继续,您必须暂时放弃锁定。否则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规定。在那里你可以使用ActorsSoftware transactional memory,它可以很容易地编写,并且可以避免大部分并发问题。