java - 如何从另一个线程中唤醒一个线程?

标签 java wait observer-pattern

为了说明我的问题,由于我们正处于复活节彩蛋时期,以下是 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/

相关文章:

java - 存储学生分数和排名的最佳数据结构

java - PUT 方法中路径变量为空时的 HTTP 响应

java - 调用wait()时发生IllegalMonitorStateException

javascript - Meteor 服务器中的 Braintree Transaction.search

java - 当创建 JDialog 并将模式设置为 true 时,我的线程似乎永远锁定?

android - 关于谷歌最新架构指南的 MVVM 模式,如何将数据(通知)从存储库类传递到模型 View 类

java - 寻求对 Function<T,R> 的深入理解

android - RxJava - 订阅者只运行一次吗

cocoa - 如何查看 cocoa 中的对象正在观察什么

java - HTC M8 (6.0.1) : TreeSet<Long>. 包含(长)抛出类转换异常: "cannot cast Double to Long"