通知在不同对象中运行的线程

时间:2013-07-25 11:33:49

标签: java multithreading design-patterns concurrency thread-safety

我仔细阅读了Oracle文档,但找不到针对我的问题的设计模式解决方案。我有两个匿名线程,一个需要通知另一个。

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

}

MyClass有两个不同的功能,每个功能都会启动一个匿名线程。 B人希望被他的妻子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();


    }

}

不幸的是,B睡着了,不能被他的妻子A吵醒。

计划的输出:

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(床上没有其他妻子所以通知功能在这里没用。)

此问题的正确设计模式是什么?

6 个答案:

答案 0 :(得分:1)

两个线程都需要使用相同的信号量对象进行锁定。

目前,您的代码中的锁定位于两个不同的对象上 - Runnable创建的a锁定了自身,b也是如此,因此当您调用{{ 1}}没有对象等待锁通知。

同步块内的notifyAll也存在问题。

更改您的代码,以便在使用Thread.sleep关键字时获得锁定:

synchronized

答案 1 :(得分:0)

这些线程之间需要共享ReentrantLock,可能作为类变量。线程A首先锁定锁,然后进入休眠状态,线程B将其锁定。线程A通过解锁锁定来唤醒线程B.您也可以使用semaphore

答案 2 :(得分:0)

基本点是wait(),应在单个对象监视器上调用notify()notifyAll()以进行线程同步。我会做这样的事情

在我的代码中,MyClassa()b()实例方法synchronized。因此,调用这些方法的实例将变为隐式监视器。我正与MyClass obj分享{strong>相同实例(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(); } } } 次实施

{{1}}

答案 3 :(得分:0)

您需要让线程共享一个公共对象来调用wait()/ notify()方法。现在你在this对象上调用它们,在这两种情况下它们都是自己的Thread对象。

另请注意,您还需要在公共对象上进行同步,因此您不能只在您的run()方法上进行同步。

答案 4 :(得分: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()的线程获得锁定并休眠1000ms。同时调用b()但它的线程必须等到a()的线程释放锁之后才能打印Thank you for waking me up

如果您始终在a()之前致电b(),这将有效。否则,如果b()首先获得锁定,那么Thank you for waking me up将在a() sleep之前执行。

答案 5 :(得分:0)

您的代码中存在两个问题。

  1. 正如其他人所说。你需要使用相同的锁来使用 通知并等待。您正在使用不同的对象进行等待和通知 是他们各自的线程实例。 您的以下代码使用MyClass.this

    运行

    尝试{                     等待();                 } catch(InterruptedException e){

  2. 即使您使用正确的锁,您的代码还有另一个问题。哪个我 认为你试图通过线程A中的Thread.sleep(1000)遇到。这个问题 被称为Missed Notifications ,即你的threadA可以在你的threadB执行其wait()方法之前完成,这将导致threadB的无限睡眠。

  3. 解决上述问题的方法是使用锁存器。尝试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();
        }
    
    }
    
相关问题