我仔细阅读了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(床上没有其他妻子所以通知功能在这里没用。)
此问题的正确设计模式是什么?
答案 0 :(得分:1)
两个线程都需要使用相同的信号量对象进行锁定。
目前,您的代码中的锁定位于两个不同的对象上 - Runnable
创建的a
锁定了自身,b
也是如此,因此当您调用{{ 1}}没有对象等待锁通知。
同步块内的notifyAll
也存在问题。
更改您的代码,以便在使用Thread.sleep
关键字时获得锁定:
synchronized
答案 1 :(得分:0)
这些线程之间需要共享ReentrantLock,可能作为类变量。线程A首先锁定锁,然后进入休眠状态,线程B将其锁定。线程A通过解锁锁定来唤醒线程B.您也可以使用semaphore。
答案 2 :(得分:0)
基本点是wait()
,应在单个对象监视器上调用notify()
或notifyAll()
以进行线程同步。我会做这样的事情
在我的代码中,MyClass
有a()
和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)
您的代码中存在两个问题。
正如其他人所说。你需要使用相同的锁来使用 通知并等待。您正在使用不同的对象进行等待和通知 是他们各自的线程实例。 您的以下代码使用MyClass.this
运行尝试{ 等待(); } catch(InterruptedException e){
即使您使用正确的锁,您的代码还有另一个问题。哪个我 认为你试图通过线程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();
}
}