Java中是否有“阻止直到条件变为真”函数?

2022-08-31 10:21:07

我正在为服务器编写一个侦听器线程,目前我正在使用:

while (true){
    try {
        if (condition){
            //do something
            condition=false;
        }
        sleep(1000);

    } catch (InterruptedException ex){
        Logger.getLogger(server.class.getName()).log(Level.SEVERE, null, ex);
    }
}

使用上面的代码,我遇到了运行函数占用所有CPU时间循环的问题。睡眠功能有效,但这似乎是一个临时的解决办法,而不是一个解决办法。

有没有一些函数会阻塞,直到变量“condition”变成“true”?还是连续循环是等待变量值变化的标准方法?


答案 1

像这样的轮询绝对是最不受欢迎的解决方案。

我假设您有另一个线程将执行某些操作以使条件为真。有几种方法可以同步线程。在您的案例中,最简单的方法是通过对象发送通知:

主线程:

synchronized(syncObject) {
    try {
        // Calling wait() will block this thread until another thread
        // calls notify() on the object.
        syncObject.wait();
    } catch (InterruptedException e) {
        // Happens if someone interrupts your thread.
    }
}

其他线程:

// Do something
// If the condition is true, do the following:
synchronized(syncObject) {
    syncObject.notify();
}

syncObject本身可以是一个简单的.Object

线程间通信还有许多其他方法,但使用哪种方法取决于您正在执行的操作。


答案 2

EboMike的答案Toby的答案都走在正确的轨道上,但它们都包含一个致命的缺陷。该漏洞称为丢失通知

问题是,如果一个线程调用 ,它根本不会做任何事情,除非其他线程已经在调用中休眠。对象 不记得它已收到通知。foo.notify()foo.wait()foo

不允许调用或除非线程在 foo 上同步,这是有原因的。这是因为避免丢失通知的唯一方法是使用互斥锁保护条件。当它正确完成时,它看起来像这样:foo.wait()foo.notify()

消费者线程:

try {
    synchronized(foo) {
        while(! conditionIsTrue()) {
            foo.wait();
        }
        doSomethingThatRequiresConditionToBeTrue();
    }
} catch (InterruptedException e) {
    handleInterruption();
}

生产者线程:

synchronized(foo) {
    doSomethingThatMakesConditionTrue();
    foo.notify();
}

更改条件的代码和检查条件的代码都在同一对象上同步,并且使用者线程在等待之前显式测试条件。当条件已经为真时,消费者无法错过通知并最终永远停留在呼叫中。wait()

另请注意,位于循环中。这是因为,在一般情况下,当使用者重新获取锁并唤醒时,其他一些线程可能再次使条件变为假。即使这在您的程序中是不可能的,在某些操作系统中,即使尚未调用,也可以返回。这被称为虚假唤醒,它被允许发生,因为它使等待/通知更容易在某些操作系统上实现。wait()foofoo.wait()foo.notify()