2016-08-19 8 views
4

Так что я работал над простым примером wait/notify в Java и по какой-то причине я не смог заставить его работать должным образом. Если кто-то может понять, что может быть проблемой, это было бы очень признательно!notify() и notifyAll() не работает в моем Java-коде

class producer implements Runnable { 
    StringBuffer sb; 
    producer() { 
     sb=new StringBuffer(""); 
    } 

    public void run() { 
     synchronized(sb) { 
      for(int i = 0; i < 10; i++) { 
       try { 
        sb.append(i+" "); 
        System.out.println("Appending ... "); 
       } catch (Exception e) {} 
      } 
      sb.notify(); 
     } 
    } 
} 

class consumer implements Runnable { 
    producer p; 
    consumer(producer pp) { 
     this.p = pp; 
    } 

    public void run() { 
     System.out.println("Rreached"); 
     synchronized(p.sb) { 
      try { 
       p.sb.wait(); 
      } catch (Exception e) {} 
      System.out.println(p.sb); 
     } 
    } 
} 

class Thread_Comunication { 
    public static void main (String [] args) { 
     producer p = new producer(); 
     consumer c = new consumer(p); 

     Thread t1 = new Thread(p); 
     Thread t2 = new Thread(c); 
     t1.start(); 
     t2.start(); 
    } 
} 

Выход:

Appending ... 
Rreached // randome Position 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 

Так почему нить t1 не разбудить t2 или я что-то отсутствует совсем другое?

+1

По крайней мере, положить 'e.printStackTrace();' в 'catch' блоков иначе вы никогда не будете знать, если произошло исключение. – Jesper

+1

Примечание. Соглашения о языке Java - это то, что имена классов начинаются с имени имени (таким образом, 'производитель 'не является хорошим именем класса). Символы не используются в именах классов, методах или пакетах, только в именах констант, которые являются полностью-капитальными. Классы имеют имена типа «ThreadCommunication», а не «Tread_Comunication». Также рекомендуется указывать переменные значимые имена, а не «p» и «pp». – RealSkeptic

+0

Закрытый избиратель: опубликованный код воспроизводит проблему (по крайней мере, насколько это возможно, поскольку код зависит от состояния гонки, которое может или не может возникнуть по усмотрению планировщика). Между опубликованным кодом и показанным выходом полезно понять, что здесь происходит. –

ответ

3

Уведомление ничего не делает, если другой поток не ждет. Ваш код зависит исключительно от уведомления (где ему нужна переменная условия) и полагается на пользователя, работающего перед производителем, чтобы он работал.

В соответствии с вашим результатом происходит, что производитель работает первым; он будет выполняться целиком до того, как потребитель получит шанс на запуск. (Для того, чтобы потребитель мог запускать его, он должен получить блокировку на sb, на которую продюсер держится.) Производитель вызывает уведомление, но нить не ждет, поэтому он не действует. Затем потребитель ждет и никакого уведомления не приходит, поэтому он висит бесконечно.

Если потребитель работает первым, тогда код будет нормально завершен.

Избегайте написания кода, который зависит от одного потока, который выполняется перед другим, потому что у вас нет контроля над тем, что выполняется в первую очередь. Когда вы ждете, вам нужно сделать это внутри цикла, который проверяет состояние. Одна из нескольких причин этого заключается в том, что если условие задано до того, как поток начнет ждать, поток может знать, что не ждать.

Изменение кода использовать условие:

import java.io.*; 

class producer implements Runnable { 
    StringBuffer sb; 
    boolean done = false; 
    producer() { 
     sb=new StringBuffer(""); 
    } 

    public void run() { 

     synchronized(sb) { 

      for(int i=0;i<10;i++) { 
       try { 
        sb.append(i+" "); 
        System.out.println("Appending ... "); 
       } catch (Exception e) {} 
      } 
      sb.notify(); 
      done = true; 
     } 


    } 
} 

class consumer implements Runnable { 
    producer p; 
    consumer(producer pp) { 
     this.p=pp; 
    } 

    public void run() { 
     System.out.println("Rreached"); 
     synchronized(p.sb) { 
      try { 
       while (!p.done) { 
       p.sb.wait(); 
       } 
      } catch (Exception e) {} 

      System.out.println(p.sb); 
     } 
    } 
} 


public class Communication { 
    public static void main (String [] args) throws Exception { 
     producer p= new producer(); 
     consumer c= new consumer(p); 

     Thread t1= new Thread(p); 
     Thread t2= new Thread(c); 
     t2.start(); 
     t1.start(); 
    } 
} 
1

Так я работал на простом ожидания/уведомить пример Java и по какой-то причине я не смог заставить его работать правильно ,

Проблема с вашим кодом в том, что notify() не сохраняется. Если producer входит в блок сначала, тогда consumer не сможет ввести его и добраться до wait до тех пор, пока producer не выйдет из цикла и не завершит его. Поскольку все notify звонков случаются внутри из synchronized блока, к тому времени consumer добираются до wait(), то producer не закончен, и больше не вызывая notify. Это означает, что consumer подвешен.

Даже если вы начинаете consumer первый, вы все еще есть условие гонки, что может привести к producer, чтобы попасть в его synchronized блок первой - это особенно актуально, потому что consumer называет System.out.println(), которая занимает много времени. Хотя это и не «исправить», если вы замедлите producer с Thread.sleep(100) до synchronized вызова, вы должны увидеть, что теперь он работает, потому что consumer получает его wait()до на producer замки его.

Есть несколько способов, чтобы вы могли исправить это. Обычно мы используем wait/notify и другую переменную, которую должен проверить consumer. В вашем случае это может быть переменная sb, так как она защищена. Так consumer может сделать что-то вроде:

synchronized (p.sb) { 
    try { 
     // do we need to wait to see if the producer added stuff? 
     if (p.sb.length() == 0) { 
      p.sb.wait(); 
     } 
    } catch (InterruptedException e) { 
     // this is always a good pattern to preserve the interrupt flag 
     Thread.currentThread().interrupt(); 
     return; 
    } 
}