我有一个包含工作项的队列,我想让多个线程并行处理这些项目。 当处理工作项时,可能会导致新的工作项。我遇到的问题是我无法找到如何确定是否已完成的解决方案。工人看起来像那样:
public class Worker implements Runnable {
public void run() {
while (true) {
WorkItem item = queue.nextItem();
if (item != null) {
processItem(item);
}
else {
// the queue is empty, but there may still be other workers
// processing items which may result in new work items
// how to determine if the work is completely done?
}
}
}
}
这实际上似乎是一个非常简单的问题,但我不知所措。实现它的最佳方法是什么?
感谢
澄清: 工作线程必须在没有人处理项目时终止,但只要其中至少有一个仍在工作,他们就必须等待,因为它可能会导致新的工作项。
答案 0 :(得分:3)
使用ExecutorService可以让您等待所有任务完成:ExecutorService, how to wait for all tasks to finish
答案 1 :(得分:0)
我建议等待/通知电话。在else的情况下,您的工作线程将等待一个对象,直到队列通知还有更多工作要做。当工作人员创建新项目时,它会将其添加到队列中,并且队列调用通知工作者正在等待的对象。其中一个人会醒来消耗新物品。
类Object的方法wait,notify和notifyAll支持从一个线程到另一个线程的有效控制转移。消耗计算量,而不是简单地“旋转”(反复锁定和解锁对象以查看某个内部状态是否已经改变),线程可以使用等待暂停自身,直到另一个线程使用notify唤醒它为止。这在线程具有生产者 - 消费者关系(在共同目标上积极合作)而不是互斥关系(在共享公共资源时试图避免冲突)的情况下尤其适用。
答案 2 :(得分:0)
我会查看比wait / notify更高级别的内容。要做到正确并避免死锁是非常困难的。你看过java.util.concurrent.CompletionService<V>
了吗?你可以有一个更简单的经理线程来轮询服务,take()
结果,可能包含也可能不包含新的工作项。
答案 3 :(得分:0)
使用包含BlockingQueue项的项目以及跟踪当前正在处理的所有元素的同步集:
BlockingQueue<WorkItem> bQueue;
Set<WorkItem> beingProcessed = new Collections.synchronizedSet(new HashMap<WorkItem>());
bQueue.put(workItem);
...
// the following runs over many threads in parallel
while (!(bQueue.isEmpty() && beingProcessed.isEmpty())) {
WorkItem currentItem = bQueue.poll(50L, TimeUnit.MILLISECONDS); // null for empty queue
if (currentItem != null) {
beingProcessed.add(currentItem);
processItem(currentItem); // possibly bQueue.add(newItem) is called from processItem
beingProcessed.remove(currentItem);
}
}
编辑:正如@Hovercraft Full Of Eels建议的那样,ExecutorService
可能是您真正应该使用的。您可以随时添加新任务。您可以半忙等待executorService.awaitTermination(time, timeUnits)
定期间隔终止所有任务,然后杀死所有线程。
答案 4 :(得分:0)
这是解决问题的队列的开头。基本上,您需要在流程工作中跟踪新工作和。
public class WorkQueue<T> {
private final List<T> _newWork = new LinkedList<T>();
private int _inProcessWork;
public synchronized void addWork(T work) {
_newWork.add(work);
notifyAll();
}
public synchronized T startWork() throws InterruptedException {
while(_newWork.isEmpty() && (_inProcessWork > 0)) {
wait();
if(!_newWork.isEmpty()) {
_inProcessWork++;
return _newWork.remove(0);
}
}
// everything is done
return null;
}
public synchronized void finishWork() {
_inProcessWork--;
if((_inProcessWork == 0) && _newWork.isEmpty()) {
notifyAll();
}
}
}
你的工人看起来大致如下:
public class Worker {
private final WorkQueue<T> _queue;
public void run() {
T work = null;
while((work = _queue.startWork()) != null) {
try {
// do work here...
} finally {
_queue.finishWork();
}
}
}
}
一个技巧是你需要在启动任何工作人员之前添加第一个工作项(否则他们将立即退出)。