Java 中是否存在“条件变为真之前的块”函数?

新手上路,请多包涵

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

 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 时间循环的问题。睡眠功能有效,但它似乎是权宜之计,而不是解决方案。

是否有一些函数会阻塞,直到变量“条件”变为“真”?还是持续循环等待变量值改变的标准方法?

原文由 Rolan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 407
2 个回答

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

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

主线程:

 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

线程间通信还有许多其他方式,但使用哪一种方式取决于您具体在做什么。

原文由 EboMike 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

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

消费者线程:

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

生产者线程:

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

改变条件的代码和检查条件的代码都在同一个对象上同步,消费者线程在等待之前显式地测试条件。当条件已经为真时,消费者无法错过通知并最终永远陷入 wait() 调用。

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

原文由 Solomon Slow 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题