繁体   English   中英

在 Java 中等待一个事件——有多难?

[英]Waiting for an event in Java - how hard is it?

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

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 时会发生什么(出于“某种原因”,未调用 set,例如异常处理中的错误)?

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

但是我担心存在一些竞争条件,如果多个线程正在等待()而另一个线程正在等待()s 和 reset()s。

题:

Java API 中是否没有类似于 Windows 事件行为的内容? 或者,如果你鄙视Windows,那么像golang的WaitGroup(即允许countUp())的CountDownLatch? 任何事物?

如何手动完成:

由于虚假唤醒,线程 2 不能简单地等待,并且在 Java 中无法知道 Object.wait() 返回的原因。 所以我需要一个条件变量来存储事件是否有信号。 主题 2:

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

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

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

还是我需要把代码复制粘贴一遍?

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

例如

boolean ready = false;

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


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

使用这种方法,线程 1 还是线程 2 先获取锁并不重要。

如果您在未设置值或检查值的情况下使用通知或等待,某些编码分析工具会向您发出警告。

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

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

(在您的主题 2 中)

编辑:在循环外同步移动。

最简单的方法就是说

firstThread.join();

这将一直阻塞,直到第一个线程终止。

但是您可以使用等待/通知来实现相同的功能。 不幸的是,你还没有发布你真正的代码片段,但我想如果当你调用通知时等待没有退出,它就会发生,因为你没有将它们都放入synchronized块中。 注意synchronized块的“参数”必须与等待/通知对相同。

我会在两个线程之间使用BlockingQueue 使用waitnotify是 5 分钟前;)

enum Event {
  Event,
  Stop;
}

BlockingQueue<Event> queue = new LinkedBlockingQueue<Event>();

// Thread 1
try {
  while(true) {
    ...do something...
    queue.put(Event.Event);
    ...wait for some condition that might never happen...
    ...
  }
} finally {
  // Tell other thread we've finished.
  queue.put(Event.Stop};
}

// Thread 2
...
switch ( queue.take() ) {
  case Event:
       ...
       break;

  default:
       ...
       break;
}

似乎只有丑陋的解决方案。 我使用 AtomicBoolean 作为标志和一些睡眠来解决它,以防止高 CPU 使用率和意外丢失事件超时......

这是我的代码:在线程类中的某处:

private static final int WAIT_DELAY_MS_HACK      = 5000; //ms
private static final AtomicBoolean NeedToExecute = new AtomicBoolean(false);

在工作线程中,需要发送唤醒信号:

public static final void SendSignalToExecute(){
    synchronized(NeedToExecute){
        NeedToExecute.set(true);
        NeedToExecute.notify();
    }
}

在必须等待信号的线程中:

//To prevent infinite delay when notify was already lost I use WAIT_DELAY_MS_HACK in wait(). 
//To prevent false interruption on unknown reason of JM I use while and check of AtomicBoolean by NeedToExecute.get() in it.
//To prevent high CPU usage in for unknown persistant interruption in wait I use additional sleep():
while (!NeedToExecute.get()){ 
    synchronized(NeedToExecute){
        try {
            NeedToExecute.wait(WAIT_DELAY_MS_HACK); //if notify() was sent before we go into wait() but after check in while() it will lost forever... note that NeedToExecute.wait() releases the synchronized lock for other thread and re-acquires it before returning
        } catch (InterruptedException ex) { //here also may be sleep or break and return
        }
    }
    sleep(100); //if wait() will not wait - must be outside synchronized block or it may cause freeze thread with SendSignalToExecute()... :(
 }
 NeedToExecute.set(false); //revert back to reenter check in next iteration, but I use it for one waited thread it cycle "do ... wait" if you use multiple thread you need to synchronise somehow this revert

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM