Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 灵活倒计时锁存器?_Java_Multithreading_Concurrency_Countdownlatch_Phaser - Fatal编程技术网

Java 灵活倒计时锁存器?

Java 灵活倒计时锁存器?,java,multithreading,concurrency,countdownlatch,phaser,Java,Multithreading,Concurrency,Countdownlatch,Phaser,我已经两次遇到这样一个问题:生产者线程生成N个工作项,将它们提交给ExecutorService,然后需要等待,直到所有N个项都被处理完毕 注意事项 N事先不知道。如果是这样的话,我只需要创建一个倒计时闩锁,然后让生产者线程等待(),直到所有工作完成 使用CompletionService是不合适的,因为尽管我的生产者线程需要阻塞(即通过调用take()),但无法发出所有工作已完成的信号,从而导致生产者线程停止等待 我目前最喜欢的解决方案是使用整数计数器,并在提交工作项时增加该计数器,在处理

我已经两次遇到这样一个问题:生产者线程生成N个工作项,将它们提交给
ExecutorService
,然后需要等待,直到所有N个项都被处理完毕

注意事项

  • N事先不知道。如果是这样的话,我只需要创建一个
    倒计时闩锁
    ,然后让生产者线程
    等待()
    ,直到所有工作完成
  • 使用
    CompletionService
    是不合适的,因为尽管我的生产者线程需要阻塞(即通过调用
    take()
    ),但无法发出所有工作已完成的信号,从而导致生产者线程停止等待
我目前最喜欢的解决方案是使用整数计数器,并在提交工作项时增加该计数器,在处理工作项时减少该计数器。在完成所有N个任务的子任务之后,我的生产者线程将需要等待一个锁,在收到通知时检查
计数器==0
。如果使用者线程已递减计数器且新值为0,则需要通知生产者

有没有更好的方法来解决这个问题,或者在
java.util.concurrent
中有没有合适的构造,我应该使用它,而不是“自己滚动”


提前感谢。

您当然可以使用受
原子引用保护的
倒计时锁存器
,这样您的任务就可以打包了:

public class MyTask extends Runnable {
    private final Runnable r;
    public MyTask(Runnable r, AtomicReference<CountDownLatch> l) { this.r = r; }

    public void run() {
        r.run();
        while (l.get() == null) Thread.sleep(1000L); //handle Interrupted
        l.get().countDown();
    }
}

我假设您的生产者不需要知道队列何时为空,但需要知道最后一个任务何时完成

我会向消费者添加一个
waitforWorkDone(producer)
方法。生产者可以添加其N个任务并调用wait方法。如果工作队列不为空且此时没有任务正在执行,wait方法将阻止传入线程


waitfor锁上的使用者线程
notifyAll()
,如果其任务已完成,队列为空,并且没有执行任何其他任务。

我已使用ExecutorCompletionService执行类似的操作:

ExecutorCompletionService executor = ...;
int count = 0;
while (...) {
    executor.submit(new Processor());
    count++;
}

//Now, pull the futures out of the queue:
for (int i = 0; i < count; i++) {
    executor.take().get();
}
ExecutorCompletionService executor=。。。;
整数计数=0;
而(…){
执行者提交(新处理器());
计数++;
}
//现在,将期货从队列中拉出:
for(int i=0;i
这涉及到保留已提交任务的队列,因此如果您的列表任意长,您的方法可能会更好

但是一定要使用一个原子整数来协调,这样你就可以在一个线程中增加它,在工作线程中减少它。

看起来它对你很有用。它计划在Java7中发布,但最稳定的版本可以在的兴趣小组网站上找到

相位器是一种美化的循环屏障。你可以注册N个政党,当你准备好等待他们在特定阶段的进展

关于其工作原理的快速示例:

final Phaser phaser = new Phaser();

public Runnable getRunnable(){
    return new Runnable(){
        public void run(){
            ..do stuff...
            phaser.arriveAndDeregister();
        }
    };
}
public void doWork(){
    phaser.register();//register self
    for(int i=0 ; i < N; i++){
        phaser.register(); // register this task prior to execution 
        executor.submit( getRunnable());
    }
    phaser.arriveAndAwaitAdvance();
}
最终相位器相位器=新相位器();
public Runnable getRunnable(){
返回新的Runnable(){
公开募捐{
…做事。。。
相位器。到达并注销();
}
};
}
公共工作{
phaser.register();//寄存器自身
对于(int i=0;i
您所描述的与使用标准信号量非常相似,但使用了“向后”

  • 您的信号量以0开始
  • 每个作业单元完成后发放一份许可证
  • 您通过等待获得N个许可证来阻止

此外,您还可以灵活地仅获取M独立Java 8+方法,它使用单程
相位器
<代码>迭代器
/
Iterable
变体完全相同

public static <X> int submitAndWait(Stream<X> items, Consumer<X> handleItem, Executor executor) {
    Phaser phaser = new Phaser(1); // 1 = register ourselves
    items.forEach(item -> {
            phaser.register(); // new task
            executor.execute(() -> {
                handleItem.accept(item);
                phaser.arrive(); // completed task
            });
    });
    phaser.arriveAndAwaitAdvance(); // block until all tasks are complete
    return phaser.getRegisteredParties()-1; // number of items
}
...
int recognised = submitAndWait(facesInPicture, this::detectFace, executor)
public static int submitAndWait(流项目、消费者handleItem、执行者){
移相器移相器=新移相器(1);//1=注册我们自己
项目。forEach(项目->{
phaser.register();//新任务
执行者。执行(()->{
handleItem.验收(项目);
phaser.arrival();//已完成任务
});
});
phaser.arriveAndWaitAdvance();//阻塞,直到所有任务完成
return phaser.getRegisteredParties()-1;//项目数
}
...
int recogned=submitAndWait(facesInPicture,this::detectFace,executor)

仅供参考。这对于单个事件来说很好,但如果同时调用,则涉及
ForkJoinPool
的解决方案将阻止父线程阻塞。

生产者在什么时间点知道有多少工作项?最后一个项目何时生产?您当前的解决方案可能会遇到竞争条件:生产项目1->计数器+++->处理项目1->计数器-->生产项目2。由于在制作人制作下一个项目之前计数器已经递减,制作人认为他已经准备好了。@rwwilden:这种情况可能会发生,你是对的。然而,我的制作人只会在提交所有工作项后检查/等待计数器,因此在这种特殊情况下它并不代表竞争条件。为什么在这种情况下用AtomicReference包装CountDownLatch会对您有所帮助?引用不需要被保护,因为它在其构造函数中被传递给MyTask,并且此后不会更改。另外,
final Phaser phaser = new Phaser();

public Runnable getRunnable(){
    return new Runnable(){
        public void run(){
            ..do stuff...
            phaser.arriveAndDeregister();
        }
    };
}
public void doWork(){
    phaser.register();//register self
    for(int i=0 ; i < N; i++){
        phaser.register(); // register this task prior to execution 
        executor.submit( getRunnable());
    }
    phaser.arriveAndAwaitAdvance();
}
public static <X> int submitAndWait(Stream<X> items, Consumer<X> handleItem, Executor executor) {
    Phaser phaser = new Phaser(1); // 1 = register ourselves
    items.forEach(item -> {
            phaser.register(); // new task
            executor.execute(() -> {
                handleItem.accept(item);
                phaser.arrive(); // completed task
            });
    });
    phaser.arriveAndAwaitAdvance(); // block until all tasks are complete
    return phaser.getRegisteredParties()-1; // number of items
}
...
int recognised = submitAndWait(facesInPicture, this::detectFace, executor)