0

我已经仔细阅读了 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(床上没有其他妻子,所以通知功能在这里没用)。

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

4

6 回答 6

1

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

目前,您的代码中的锁位于两个不同的对象上 - Runnablecreated bya对自身有一个锁,并且与 相同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();
}
于 2013-07-25T11:54:19.497 回答
0

这些线程之间需要一个共享的ReentrantLock,也许是一个类变量。线程 A 先锁定锁,然后进入睡眠状态,线程 B 锁定它。线程 A 通过解锁锁唤醒线程 B。您也可以为此使用信号量。

于 2013-07-25T11:49:38.337 回答
0

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

在我的代码中,MyClasshasa()b()instance 方法synchronized。因此,调用这些方法的实例将成为隐式监视器。我与 2 个实现共享相同的(即)实例MyClassobjRunnable

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

}
于 2013-07-25T11:49:51.943 回答
0

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

另请注意,您也需要在公共对象上同步,因此您不能只将同步放在 run() 方法上。

于 2013-07-25T11:49:54.953 回答
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()' 的线程获取锁并休眠 1000 毫秒。同时b()会被调用,但它的线程必须等到a()线程释放锁才能打印Thank you for waking me up

如果您总是a()在之前调用,这将起作用b()。否则,如果b()先获取锁,它Thank you for waking me up会在之前执行a() sleep

于 2013-07-25T11:54:17.137 回答
0

您的代码中有两个问题。

  1. 正如其他人所建议的那样。您需要使用相同的锁来使用通知和等待。您正在使用不同的对象来等待和通知它们各自的线程实例。您的以下代码使用 MyClass.this 运行

    尝试{等待();} 捕捉(InterruptedException e){

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

解决上述两个问题的方法是使用闩锁。试试 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();
    }

}
于 2013-08-02T10:01:33.197 回答