在Java中等待事件 - 这有多难?

2022-09-02 01:19:46

我有一个线程会不时更新其状态,我希望第二个线程能够等待第一个线程完成。像这样:

Thread 1:
    while(true) {
        ...do something...
        foo.notifyAll()
        ...wait for some condition that might never happen...
        ...
    }

Thread 2:
    ...
    foo.wait();
    ...

现在这看起来不错,除非线程 1 的 notifyAll() 在线程 2 的 wait() 之前运行,在这种情况下,线程 2 会等到线程 1 再次发出通知(这可能永远不会发生)。

我可能的解决方案:

a)我可以使用CountDownLatch或Future,但两者都有一个问题,即它们本质上只运行一次。也就是说,在线程 1 的 while 循环中,我需要创建一个新的 foo 来等待每次,而线程 2 需要询问要等待哪个 foo。我对简单地写作有一种不好的感觉

while(true) {
   foo = new FutureTask(); 
   ...
   foo.set(...);
   ...wait for a condition that might never be set...
   ...
}

由于我担心在foo = new FutureTask(),当有人等待旧的foo时会发生什么(由于“某种原因”,设置未被调用,例如异常处理中的错误)?

b)或者我可以使用信号量:

class Event {
   Semaphore sem;
   Event() { sem = new Semaphore(1); sem . }
   void signal() { sem.release(); }
   void reset() { sem.acquire(1); }
   void wait() { if (sem.tryAcquire(1)) { sem.release(); } }
}

但是我担心存在一些争用条件,如果多个线程正在等待()ing,而另一个线程发出信号并重置()s。

问题:

Java API 中是否有任何内容与 Windows 事件行为相似?或者,如果你鄙视Windows,比如golang的WaitGroup(即允许countUp()的CountDownLatch)?什么?

如何手动操作:

线程 2 不能简单地等待,因为虚假唤醒,在 Java 中,没有办法知道为什么 Object.wait() 返回。因此,我需要一个条件变量来存储事件是否发出信号。线程 2:

synchronized(foo) {
    while(!condition) {
        foo.wait();
    }
}

当然,线程 1 在同步块中将条件设置为 true。感谢weekens的提示!

是否有包装该行为的现有类?

还是我需要将代码全部复制并粘贴到所有位置?


答案 1

标准做法是在执行 notifyAll 时更改某些状态,并在执行 wait() 时检查某些状态。

例如:

boolean ready = false;

// thread 1
synchronized(lock) {
    ready = true;
    lock.notifyAll();
}


// thread 2
synchronized(lock) {
    while(!ready) 
        lock.wait();
}

使用此方法时,线程 1 或线程 2 首先获取锁并不重要。

如果您使用通知或等待而不设置值或检查值,某些编码分析工具会给您一个警告。


答案 2

您可以使用带超时的 wait(),在这种情况下,您不会冒永远等待的风险。另请注意,即使根本没有 notify() 也可能返回 wait(),因此,您需要将等待包装在某个条件循环中。这是Java中等待的标准方式。

synchronized(syncObject) {
    while(condition.isTrue()) {
        syncObject.wait(WAIT_TIMEOUT);
    }
}

(在您的线程 2 中)

编辑:已同步移动到循环之外。