如何处理可能创建新工作项的多个工作线程

时间:2011-07-01 13:20:23

标签: java concurrency

我有一个包含工作项的队列,我想让多个线程并行处理这些项目。 当处理工作项时,可能会导致新的工作项。我遇到的问题是我无法找到如何确定是否已完成的解决方案。工人看起来像那样:

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?
      }
    }
  }
}

这实际上似乎是一个非常简单的问题,但我不知所措。实现它的最佳方法是什么?

感谢

澄清: 工作线程必须在没有人处理项目时终止,但只要其中至少有一个仍在工作,他们就必须等待,因为它可能会导致新的工作项。

5 个答案:

答案 0 :(得分:3)

使用ExecutorService可以让您等待所有任务完成:ExecutorService, how to wait for all tasks to finish

答案 1 :(得分:0)

我建议等待/通知电话。在else的情况下,您的工作线程将等待一个对象,直到队列通知还有更多工作要做。当工作人员创建新项目时,它会将其添加到队列中,并且队列调用通知工作者正在等待的对象。其中一个人会醒来消耗新物品。

  

类Object的方法wait,notify和notifyAll支持从一个线程到另一个线程的有效控制转移。消耗计算量,而不是简单地“旋转”(反复锁定和解锁对象以查看某个内部状态是否已经改变),线程可以使用等待暂停自身,直到另一个线程使用notify唤醒它为止。这在线程具有生产者 - 消费者关系(在共同目标上积极合作)而不是互斥关系(在共享公共资源时试图避免冲突)的情况下尤其适用。

Source: Threads and Locks

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

一个技巧是你需要在启动任何工作人员之前添加第一个工作项(否则他们将立即退出)。