如何在我的用例中协调线程

时间:2014-06-26 10:42:38

标签: java multithreading scala

我有以下情况:

  1. 线程A将向线程B和C发送许多任务(确切的任务数量未知)

  2. 对于每个任务,线程A将同时(异步)将其发送到B和C,然后如果B或C中的任何一个成功完成任务或两者都失败,A将继续发送下一个任务。这里的想法是尽可能避免阻塞。即,对于相同的任务,当B在C仍在处理时完成它时,A可以立即发送下一个任务,而不需要等待C来获得结果。

  3. 只要任务由另一个完成,预计B和C中较慢的一个可以跳过某些任务。例如,B可以完成任务t1 t2 t3 t4,并且C仅完成t1 t4,因为当C接收到t2和t3时,由于某种原因它仍在处理t1。

  4. 是否有适合此的线程同步构造?我正在检查java.util.concurrent.Phaser,但似乎不符合我的需要。欢迎提出任何意见,谢谢。

3 个答案:

答案 0 :(得分:2)

如果使用Future或者actor而不是Threads作为构建块,这会更容易。直接在线程上执行此操作可能会导致许多问题,因为您必须处理详细信息,例如排队传入的消息。另一个问题是,如果你想要解决的问题反映了你所要求的内容,那么我无法理解 - 执行同一任务两次的价值,2不同的线程?那只是感觉不对。

这是一个天真的非阻塞实现,可以了解所涉及的内容,但不要在实际代码中执行此操作(确实考虑更高级别的抽象):

val queue = new AtomicReference(Queue.empty[Runnable])

def worker() = new Thread(new Runnable {
  @tailrec
  def run() = {
    val currentQueue = queue.get
    if (currentQueue.nonEmpty) {
      val (task, updatedQueue) = currentQueue.dequeue
      try {
        task.run()
      } catch {
        case NonFatal(ex) =>
          ex.printStackTrace()
      }

      // if this fails, then another worker succeeded
      queue.compareAndSet(currentQueue, updatedQueue)
      // process next task in queue
      if (updatedQueue.nonEmpty) run()
    }
  }
})

@tailrec
def submitTask(task: Runnable): Unit =  {
  val currentQueue = queue.get
  val newQueue = currentQueue.enqueue(task)

  if (!queue.compareAndSet(currentQueue, newQueue))
    submitTask(task)
  else if (currentQueue.isEmpty) {
    // because of the CAS above, only 2 workers will be
    // active at the same time
    worker().start()
    worker().start()
  }
}

答案 1 :(得分:1)

我会在Balancing Workloads Across Nodes with Akka 2上使用变体,除了不为每个工作人员分配一个工作项,它会将当前工作项分配给请求工作的每个工作人员,直到工作项完成。

看起来有点矫枉过正,但这种模式可以很好地扩展,并且易于定制。我使用它在Production中有两个项目,有others。这种方法是一种“拉动”#34;方法

A"推动"使用演员的方法需要工人演员that only process the last message in their mailbox。然后,主要参与者将所有消息转发给所有工作者。

答案 2 :(得分:0)

考虑使用一些Executor,例如Executors.newFixedThreadPool()。它允许:

  • 提交将来会处理的任务;
  • 处理任务的线程不会尝试完成相同的任务;
  • 为每个提交的任务返回
  • Future接口,让您检查任务是否成功完成,仍在进行中或失败;
  • 2)中描述的阻止机制可以依赖于Future.get()方法阻塞直到任务完成/未能完成的事实来实现;

我认为这些功能涵盖了您编写的行为要求。