0

Я внимательно изучил документацию Oracle и не смог найти решение для шаблона решения для моей проблемы. У меня есть два анонимных потока, и нужно уведомить другого.Сообщить о неполадках, запущенных в разных объектах

public static void main(String[] args) { 
    MyClass obj = new MyClass(); 
    obj.a(); 
    obj.b(); 

} 

MyClass имеет две разные функции, каждая из которых запускает анонимную нить. B человек ожидает, что будить его жена, А.

public class MyClass{ 

    public MyClass(){ 

    } 

    public void a() { 
     new Thread(new Runnable(){ 

      @Override 
      public synchronized void run() { 
       System.out.println("A: I am going to sleep"); 
       try { 
        Thread.sleep(1000); 
        System.out.println("A: I slept one full day. Feels great."); 
        System.out.println("A: Hey B, wake up!"); 
        notifyAll(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 


      } 
     }).start(); 


    } 

    public void b() { 
     new Thread(new Runnable(){ 

      @Override 
      public synchronized void run() { 
       System.out.println("B: I am going to sleep. A, please wake me up."); 
       try { 
        wait(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       System.out.println("B: Thank you A for waking me up!"); 


      } 
     }).start(); 


    } 

} 

К сожалению, B спит навсегда и не может быть разбудили его жена, А.

Выход программы:

A: I am going to sleep 
B: I am going to sleep. A, please wake me up. 
A: I slept one full day. Feels great. 
A: Hey B, wake up! 

Я понимаю, что A и B работают в двух разных анонимных объектах нитей, поэтому A может уведомлять только о другом A (нет другой жены в постели, поэтому функция уведомления здесь бесполезна).

Каков правильный шаблон дизайна для этой проблемы?

ответ

1

Оба потока необходимо блокировать, используя тот же самый семафор объект.

В настоящее время блокировки в вашем коде находятся на двух разных объектах - в Runnable созданный a имеет блокировку на себя и то же самое с b, поэтому, когда вы звоните notifyAll нет ни одного объекта в ожидании блокировки уведомления.

Существует также проблема с Thread.sleep внутри синхронизированного блока.

Измените код таким образом, чтобы полученный замок, когда ключевое слово synchronized используется следующим образом:

public void a() 
{ 
    new Thread(
    new Runnable() 
    { 
     @Override 
     public void run() 
     { 
     try 
     { 
      System.out.println("A: I am going to sleep"); 
      Thread.sleep(1000); 
     } 
     catch (InterruptedException e) 
     { 
      e.printStackTrace(); 
     } 

     synchronized(MyClass.this) 
     { 
      System.out.println("A: I slept one full day. Feels great."); 
      System.out.println("A: Hey B, wake up!"); 
      MyClass.this.notifyAll(); 
     } 
     } 
    } 
).start(); 
} 

public void b() 
{ 
    new Thread(
    new Runnable() 
    { 
     @Override 
     public void run() 
     { 
     synchronized(MyClass.this) 
     { 
      System.out.println("B: I am going to sleep. A, please wake me up."); 

      try 
      { 
      MyClass.this.wait(); 
      } 
      catch (InterruptedException e) 
      { 
      e.printStackTrace(); 
      } 

      System.out.println("B: Thank you A for waking me up!"); 
     } 
     } 
    } 
).start(); 
} 
0

Между этими потоками должен быть общий ReentrantLock, возможно, как переменная класса. Нить A сначала блокирует замок, затем, чтобы заснуть, нить B блокирует его. Thread A пробуждает поток B, отпирая замок. Вы также можете использовать для этого semaphore.

0

Основной вопрос: wait() и notify() или notifyAll() следует вызывать на одном объектном мониторе для синхронизации потоков. Я бы сделал что-то вроде этого

В моем коде MyClass имеет a() и b() метод экземпляра synchronized. Таким образом, экземпляр , на который будут вызываться эти методы, станет неявных мониторов. Я делюсь с же экземпляр из MyClass (который obj) с 2 Runnable реализации

public class MyClass{ 

    public MyClass(){ 

    } 

    public synchronized void a() { 
     System.out.println("A: I am going to sleep"); 
     try { 
      Thread.sleep(5000); 
      wait(); 
      System.out.println("A: I slept one full day. Feels great."); 
      System.out.println("A: Hey B, wake up!"); 
      notifyAll(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

    } 

    public synchronized void b() { 
     System.out.println("B: I am going to sleep. A, please wake me up."); 
     notifyAll(); 
     try { 
      wait(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("B: Thank you A for waking me up!"); 

    } 

    public static void main(String [] args) { 
     MyClass obj = new MyClass(); 

     Thread t1 = new Thread(new RunnableImpl(obj, true)); 
     Thread t2 = new Thread(new RunnableImpl(obj, false)); 
     t1.start(); 
     t2.start(); 
    } 

} 

class RunnableImpl implements Runnable { 

    boolean callA; 
    MyClass obj; 

    public RunnableImpl(MyClass obj, boolean callA) { 
     this.callA = callA; 
     this.obj = obj; 
    } 


    @Override 
    public void run() { 
     if(callA) { 
      obj.a(); 
     } 
     else { 
      obj.b(); 
     } 
    } 

} 
+0

Да ладно, не можете ли вы позволить другим людям выполнять домашнее задание? Как они узнают, если вы напишите для них рабочий код. – Kayaman

+0

@ Kayaman Я бы не отправил ответ, если бы у OP был нулевой код. ОП попробовал что-то, поэтому я опубликовал возможный ответ. Наверное, так работает SO? – sanbhat

+0

@sanbhat Почему у нас есть notifyAll() в b? – poiuytrez

0

Вы должны иметь общий объект совместно нитями вызвать ожидание()/уведомляет() методы на. Теперь вы вызываете их на объект this, который в обоих случаях является их собственным объектом Thread.

Также обратите внимание, что вам также необходимо синхронизировать с общим объектом, поэтому вы не можете просто синхронизировать свои методы run().

0

Для того, чтобы разбудить один поток от другого, необходимо синхронизировать его с общим объектом. Например, вы могли бы использовать в MyClass объект нити вызываются из:

public void a() { 
    new Thread(new Runnable(){ 

     @Override 
     public synchronized void run() { 
      System.out.println("A: I am going to sleep"); 
      synchronized(MyClass.this) 
      { 
       try { 
        Thread.sleep(1000); 
        System.out.println("A: I slept one full day. Feels great."); 
        System.out.println("A: Hey B, wake up!"); 
        MyClass.this.notifyAll(); 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
      } 
     } 
    }).start(); 


} 

public void b() { 
    new Thread(new Runnable(){ 

     @Override 
     public synchronized void run() { 
      System.out.println("B: I am going to sleep. A, please wake me up."); 
      synchronized(MyClass.this) 
      { 
       System.out.println("B: Thank you A for waking me up!"); 
      } 
     } 
    }).start(); 


} 

Это сделает a() «s нить получить блокировку и сон для 1000ms. Между тем b() будет называться, но его поток должен будет ждать, пока нить a() не выпустит замок, прежде чем он сможет распечатать Thank you for waking me up.

Это будет работать, если вы всегда звоните a() до b(). В противном случае, если b() сначала заберет замок, это будет Thank you for waking me up будет выполнено до a()sleep.

0

У вас есть две проблемы в вашем коде.

  1. Как предложено другими лицами. Вам нужно взять тот же замок для использования уведомлять и ждать. Вы используете разные Объекты для ожидания и уведомляете, какие являются их соответствующими экземплярами потоков. Ваш код ниже используется MyClass.this

    try { wait(); } catch (InterruptedException e) {

  2. Есть еще одна проблема с вашим кодом, даже если вы используете правые замки. Который i думает, что вы пытаетесь встретить Thread.sleep (1000) в потоке A. Эта проблема называется Missed Notifications, т. Е. Ваш потокA может завершиться до того, как ваш threadB выполнит метод wait(), это приведет к бесконечному сну потокаB.

Решение для обеих вышеуказанных проблем заключается в использовании защелки. попробуйте CountDownLatch См. ниже

import java.util.concurrent.CountDownLatch; 

public class MyClass{ 

    CountDownLatch latch = new CountDownLatch(1); 

    public MyClass(){ 

    } 

    public void a() { 
     new Thread(new Runnable(){ 
      @Override 
      public void run() { 
       System.out.println("A: I am going to sleep"); 
       System.out.println("A: I slept one full day. Feels great."); 
       System.out.println("A: Hey B, wake up!"); 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       latch.countDown(); 
      } 
     }).start(); 
    } 

    public void b() { 
     new Thread(new Runnable(){ 
      @Override 
      public void run() { 
       System.out.println("B: I am going to sleep. A, please wake me up."); 
       try { 
        latch.await(); 
       } catch (InterruptedException e) {} 
       System.out.println("B: Thank you A for waking me up!"); 
      } 
     }).start(); 
    } 

    public static void main(String[] args) { 
     MyClass obj = new MyClass(); 
     obj.a(); 
     obj.b(); 
    } 

} 
Смежные вопросы