2013-07-01 2 views
8

Я пытаюсь проверить, как wait/notify работает в java.Wait()/notify() synchronization

Код:

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

монитор объекта блокировки должны быть выполнены одной ссылки одного и того же замка ...

В вашем примере вы 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"); 
     } 
    } 
+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 будет вызван только после того, как вторичная нить покинет блок 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"); 
     } 
    } 
} 

Однако при использовании этих примитивов низкого уровня может быть очень подвержен ошибкам, и я 'd препятствовать их использованию. Вместо этого я предлагаю вам использовать для этого примитивы 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, которые легко скомпилированы и избавят вас от большинства проблем параллелизма.