poiuytrez
poiuytrez

Reputation: 22518

Notify threads running in different objects

I have read carefully the Oracle documentation and I could not find a design pattern solution for my issue. I have two anonymous threads and one needs to notify the other.

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

}

The MyClass has two different functions, each one launches an anonymous thread. The B person expects to be woken up by his wife, A.

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();


    }

}

Unfortunately, B sleeps forever and could not be woken up by his wife, A.

Output of the program:

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!

I understand that A and B are running in two different anonymous threads objects, so A could notify only other A (there are not other wife in the bed so the notify function is useless here).

What is the correct design pattern for this issue?

Upvotes: 0

Views: 1233

Answers (6)

veritas
veritas

Reputation: 2444

You have two problems in your Code.

  1. As suggested by others . You need to take the same lock for using notify and wait. You are using different Objects for waiting and notifying which is their respective thread instances. Your below code is run use MyClass.this

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

  2. There is another problem with your code even if you use right locks. Which i think you try to encounter by Thread.sleep(1000) in thread A. This problem is called Missed Notifications, i.e. your threadA can complete before your threadB executes its wait() method, this will result in infinite sleep of threadB.

Solution for both the above problem is to use a latch . try CountDownLatch See below

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();
    }

}

Upvotes: 0

stan0
stan0

Reputation: 11807

To be able to wake one thread from another they need to be synchronized with a common object. For example you could use the MyClass object the threads are called from:

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();


}

This will make a()'s thread acquire the lock and sleep for 1000ms. Meanwhile b() would be called but it's thread would have to wait until a()'s thread releases the lock before it can print Thank you for waking me up.

This would work if you always call a() before b(). Otherwise, if b() acquires the lock first, it's Thank you for waking me up would be executed before a() sleep.

Upvotes: 0

Nick Holt
Nick Holt

Reputation: 34321

Both threads need to lock using the same semaphore object.

Currently the locks in your code are on two different objects - the Runnable created by a has a lock on the itself and the same with b, so when you call notifyAll there are no object waiting for the lock to notify.

There's also a problem with the Thread.sleep inside the synchronized block.

Change your code so that the lock obtained when the synchronized key word is used like this:

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();
}

Upvotes: 1

sanbhat
sanbhat

Reputation: 17622

The basic point is wait() and notify() or notifyAll() should be called on a single object monitor to have thread synchronization. I would have done something like this

In my code the MyClass has a() and b() instance method synchronized. So the instance on which these methods get invoked will become implicit monitors. I am sharing the same instance of MyClass (which is obj) with 2 Runnable implementations

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();
        }
    }

}

Upvotes: 0

Kayaman
Kayaman

Reputation: 73558

You need to have a common object shared by the threads to call the wait()/notify() methods on. Now you're calling them on the this object, which in both cases is their own Thread object.

Also note that you need to synchronize on the common object too, so you can't just put synchronized on your run() methods.

Upvotes: 0

tbodt
tbodt

Reputation: 16997

There will need to be a shared ReentrantLock between these threads, perhaps as a class variable. Thread A locks the lock first, then to go to sleep, thread B locks it. Thread A awakens thread B by unlocking the lock. You can also use a semaphore for this.

Upvotes: 0

Related Questions