我正在使用Java构建一个缓冲区,它将收集许多线程请求的写入操作,并将它们作为一组刷新,例如每秒一次。我想给它一个名为waitForFlush
的方法,它将阻塞并调用它,直到下一个flush事件完成。同时,一个独立的独立线程正在刷新并在一个循环中睡觉。所以我基本上寻找一个并发结构或模式,允许许多线程在特定点阻塞,然后同时释放它们,我发现Java的内置并发原语都不是真的非常接近。到目前为止,我提出的最好的是wait / notifyAll,如下所示:
public class Buffer {
private volatile long lastFlushTime = System.currentTimeMillis();
private final Object flushMonitor = new Object();
public void waitForFlush() {
long entryTime = System.currentTimeMillis();
synchronized(flushMonitor) {
while(lastFlushTime <= entryTime) {
flushMonitor.wait();
}
}
}
public void flush() {
// do flush stuff here
synchronized(flushMonitor) {
lastFlushTime = System.currentTimeMillis();
flushMonitor.notifyAll();
}
}
}
虽然我认为这在实践中会很好用,waitForNotify()
中的同步块对我来说仍然有些不完美。理想情况下,对于此用例,您可以调用wait()
而无需在关联的对象上进行同步,并且所有被阻止的线程将在调用notifyAll()
的同一时刻释放,而不是必须一个一个地退出同步块。
所以,总的来说,是否有更好的方法来阻止并同时释放可变数量的线程(我认为,Semaphore和CountDownLatch类只适用于固定数量的线程)?
答案 0 :(得分:3)
虽然CountDownLatch可以用于Marko Topolnik和munyengm提到的一次性案例。它在循环场景中失败(即每个CDL只能await
&amp; countDown
一次。然后你可以考虑一个CyclicBarrier,但是在你的情况下会失败,因为你需要知道正在使用的线程数。
如果您可以使用Java 7,我建议使用Phaser。您可以将单线程信号发送到许多等待线程并重用。
final Phaser phaser = new Phaser(1);//register one thread to arrive
public void waitForFlush() {
int phase = phaser.getPhase();
phaser.awaitAdvance(phase);
}
public void flush() {
lastFlushTime = System.currentTimeMillis();
phaser.arrive(); //signals all waiting threads on the current phase and will increment the phase by 1
}
答案 1 :(得分:2)
如果您正确使用CountDownLatch
,我认为它可以为您完成。方法是在锁存器上创建可变数量的线程await
,并且刷新线程调用countDown
。锁存器始终初始化为1.这就是它的工作原理:
public class FlushControl
{
private volatile CountDownLatch latch = new CountDownLatch(1);
public void awaitFlush() throws InterruptedException { latch.await(); }
public void flush() {
final CountDownLatch l = latch;
latch = new CountDownLatch(1);
l.countDown();
}
}