为了说明我的问题,由于我们正处于复活节彩蛋时期,以下是 Storyboard: 第一个字符是一个时钟,定期给出时间。但是这个时钟非常喜怒无常:它不会回答询问时间的用户,而是定期通知他的所有观察者,并且这个周期是随机定义的。 我的第二个角色是一只压力很大的兔子。这只兔子除非知道时间,否则什么也做不了。当他完成 Action 后,他会再次询问时间并等待得到时间后再做其他事情。 我可以添加其他角色(一只猫、一个疯帽子制造者......),但对于这个例子来说,这是没有必要的。
因此,在 Java 中,我将拥有一个可由观察者观察的时钟,无论观察者的类型如何;还有一只兔子,它是一种观察者。我想保留“可观察/观察者”模式,因为时钟不知道也不关心谁是观察者。
这是我正在使用的类:
时钟
public class Clock implements Runnable, ClockObservable {
private Long time;
public Clock () {
}
@Override
public void run() {
while (true) {
time=System.currentTimeMillis();
update();
try {
int randomTimeUpdate=(int)( (Math.random() + 1) *500); //This clock will update randomly
Thread.sleep(randomTimeUpdate);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void update() {
for (ClockObserver observer : observers) {
observer.update(time);
}
}
}
时钟可观察
import java.util.ArrayList;
public interface ClockObservable {
public static ArrayList<ClockObserver> observers = new ArrayList<ClockObserver>();
public default void addObserver (ClockObserver observer) {
observers.add(observer);
}
public default void resetObservers () {
observers.clear();
}
public void update ();
}
时钟观察器
public interface ClockObserver {
public void update(Long time);
}
兔子
public class Rabbit implements ClockObserver {
Long time;
public Rabbit (Clock clock) {
clock.addObserver(this);
whatTimeIsIt();
}
public synchronized void whatTimeIsIt () {
while (true) {
System.out.println("What time is it ?");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("It's "+time+" -> Yepeeeee !!!! I can do something before asking the time again...");
System.out.println("-----------------------------------");
}
}
@Override
public void update(Long time) {
this.time=time;
System.out.println("Time = "+time);
//How can I notify the rabbit ?
Rabbit.this.notifyAll(); //This cause a «java.lang.IllegalMonitorStateException»
}
}
主要
public class Main {
public static void main(String[] args) {
Clock clock = new Clock();
Thread thread = new Thread (clock);
thread.start();
new Rabbit(clock);
}
}
问题是 Rabbit 类中重写的更新方法中的以下指令,该指令生成“java.lang.IllegalMonitorStateException”。
Rabbit.this.notifyAll();
事实上,Rabbit 位于主线程上,而通知位于线程 0 上。
可是,我能做什么呢?如何解决我的问题?
感谢您的所有回答。
Dr_Click
最佳答案
public void update(Long time) {
this.time=time;
System.out.println("Time = "+time);
//How can I notify the rabbit ?
Rabbit.this.notifyAll(); //This cause a «java.lang.IllegalMonitorStateException»
}
notifyAll 函数用于通知其他线程某些共享状态已更改。这里共享状态没有改变,所以没有什么可以通知其他线程的。如果您认为 this.time
是共享状态,请解释为什么非同步方法会在不持有任何锁的情况下修改它。您无法通过共享状态来做到这一点。
public synchronized void whatTimeIsIt () {
while (true) {
System.out.println("What time is it ?");
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
这里有同样的问题。您调用 wait
时没有检查您正在等待的事情是否已经发生。你在等什么?未处于您需要的状态的共享状态是什么?
想象一下,如果一个线程即将调用 wait
但调度程序延迟了它。然后,另一个线程调用notifyAll。您的线程仍会调用 wait
,因为它没有检查共享状态以查看是否应该等待。
除非等待某些共享状态具有某些值,否则不能使用wait
。除了通知另一个线程共享状态已更改之外,您不能使用 notify
。
您当前的代码没有任何意义,因为它没有等待任何事情,也没有通知任何事情。 notify
/wait
函数没有信号量的语义。他们没有自己的共享状态。您必须实现共享状态。
如果兔子处于等待状态,则在某个地方您需要一些变量来保存兔子的状态,并且需要将其设置为“等待”。然后,当你想改变兔子的状态时,你需要将该状态更改为“运行”。然后,兔子可以等待其状态设置为“正在运行”,并且另一个线程可以通知它其状态已更改。但是您没有实现任何共享状态,因此无需等待,也无需通知。当然,该状态变量需要受到锁的保护。
兔子应该有像 while (state == waiting) wait();
这样的代码,而另一个线程可以有像 state = running; 这样的代码。通知所有();
。那么兔子实际上正在等待某件事,并且另一个线程修改了一些共享状态,它可能需要通知另一个线程有关。当然,状态只能在持有锁的情况下更改或测试。
另外,为什么 update
不是一个synchronized
方法?它改变了共享的时间
。
这是另一种选择:
public synchronized void whatTimeIsIt () {
Long last_time = time; // make local copy
while (true) {
System.out.println("What time is it ?");
try {
while (time == last_time)
wait();
last_time = time;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("It's "+time+" -> Yepeeeee !!!! I can do something before asking the time again...");
System.out.println("-----------------------------------");
}
}
注意到线程现在如何等待某些东西吗?并注意到两个线程之间如何共享状态吗?
关于java - 如何从另一个线程中唤醒一个线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61224370/