如何从另一个线程唤醒一个线程?

How to wake up a thread from another thread?

为了说明我的问题,由于我们正处于彩蛋期,这里是情节提要: 第一个字符是一个时钟,它周期性地给出时间。但是这个时钟非常喜怒无常:它不回答询问时间的用户,而是定期通知他的所有观察者,并且这个时间段是随机定义的。 我的第二个角色是一只压力很大的兔子。除非他知道时间,否则这只兔子什么也做不了。当他完成他的动作时,他会再次询问时间并等待得到它再做其他事情。 我可以添加其他角色(一只猫,一个疯帽子制造者......)但对于这个例子,这是没有必要的。

因此,在 Java 中,我将拥有一个可供观察者观察的时钟,无论观察者的类型如何;和一只兔子,它是一种观察者。我想保留 «observable / observer» 模式,因为时钟不知道也不关心谁是观察者。

这是我正在使用的 class :

时钟

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

ClockObservable

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

}

ClockObserver

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 class 中的以下指令,在覆盖的更新方法中,生成了 «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 除非通知另一个线程共享状态已更改。

您当前的代码没有任何意义,因为它没有等待 for 任何东西,也没有通知 about 任何东西。 notify/wait 函数没有信号量的语义。他们没有自己的共享状态。你必须实现共享状态。

如果兔子处于等待状态,您需要在某处保存兔子状态的变量,并且需要将其设置为 "waiting"。然后,当你想改变兔子的状态时,你需要将该状态更改为"running"。然后兔子可以等待它的状态设置为 "running" 并且另一个线程可以通知它它的状态已经改变。但是您没有实现任何共享状态,因此没有什么可等待的,也没有什么可通知的。当然,那个状态变量需要用锁来保护。

兔子应该有 while (state == waiting) wait(); 这样的代码,另一个线程可以有 state = running; notifyAll(); 这样的代码。然后兔子实际上在等待 something 并且另一个线程修改了一些共享状态,它可能需要通知另一个线程 about。当然只有在持有锁的情况下才能更改或测试状态。

此外,为什么 update 不是 synchronized 方法?它改变了共享的 time

还有一个选项:

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

注意线程现在如何等待 某些东西?并注意到两个线程之间是如何共享状态的?