[英]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 。 使用wait
和notify
是 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.