代码之家  ›  专栏  ›  技术社区  ›  marc esher

Java中线程安全队列和“主/辅”程序的模式/原则

  •  11
  • marc esher  · 技术社区  · 16 年前

    这里有一个某种形式的全局“队列”,它是保存“要完成的工作”的中心位置。大概这个队列将由一种“主”对象管理。线程将被生成以查找要做的工作,当它们找到要做的工作时,它们将告诉主对象(不管是什么)将其“添加到要完成的工作队列”。

    主线程可能会在一段时间内产生其他线程,实际执行要完成的工作。一旦一个线程完成了它的工作,我希望它通知主线程工作已经完成。然后,主机可以从队列中删除此工作。

    我过去用Java做过很多线程编程,但都是在JDK1.5之前,因此我不熟悉处理这种情况的合适的新API。我知道JDK7将有fork-join,这对我来说可能是一个解决方案,但我不能在这个项目中使用早期访问产品。

    3) 为队列选择适当的数据结构。我在这里的想法是,“线程查找要做的工作”可能会发现同一个工作要做不止一次,并且它们会向主线程发送一条消息说“这里有工作”,主线程会意识到工作已经安排好,因此应该忽略该消息。我想确保我选择了正确的数据结构,这样计算就尽可能便宜。

    传统上,我会在数据库中以有限状态机的方式完成这项工作,从头到尾处理“任务”。然而,在这个问题中,我不想使用数据库,因为队列的容量和波动性都很高。另外,我希望这个尽量轻。如果可以避免的话,我不想使用任何应用服务器。

    谢谢你的指点。

    6 回复  |  直到 16 年前
        1
  •  7
  •   Denis Bazhenov    16 年前

    据我所知,你需要 ExecutorService . 执行人服务

    submit(Callable task)
    

    Future . 未来是一种阻碍工人和主人之间沟通的方式。您可以轻松地扩展此机制,使其以异步方式工作。是的,ExecutorService也像ThreadPoolExecutor一样维护工作队列。因此,在大多数情况下,您不需要为日程安排而烦恼。concurrent包已经有了线程安全队列的高效实现(ConcurrentLinked queue-nonblocking和LinkedBlockedQueue-blocking)。

        2
  •  4
  •   starblue    16 年前

    退房 java.util.concurrent 在Java库中。

    根据您的应用程序,它可能很简单,只需拼凑一些阻塞队列和ThreadPoolExecutor即可。

    Java Concurrency in Practice 布莱恩·戈茨的著作可能会有所帮助。

        3
  •  4
  •   Tim Büthe    16 年前

    首先,为什么要在工作人员开始操作这些项目后再保存它们?通常,您会有一个工作队列,工作人员会从该队列中取出项目。这也将解决“如何防止员工获得相同物品”的问题。

    1) 如何让“线程”执行 告诉他们他们的工作是 完成,大师现在可以 从队列中删除该工作

    主人可以听工人们用键盘说话 listener/observer pattern

    2) 如何有效地获得硕士学位 保证工作是永远的 预定一次。比如说 此队列有一百万个项目,并且 100件事”最有效的是什么 当它 将工作安排给下一个工人,它 获取“接下来的100件事”而不是 预定“?

    3) 选择合适的数据 队列的结构。我的想法 这里是“线程查找工作” 工作不止一次,他们会 给大师发个信,说 “这是工作”,大师会说 意识到工作已经完成 计划的,因此应该 我选择了正确的数据结构 因此,这种计算方法同样便宜

    有一个 blocking queue 从Java5开始

        4
  •  1
  •   Brian Agnew    16 年前

    不要忘记Jini和Javaspaces。你所描述的听起来很像天基架构擅长的经典生产者/消费者模式。

    制作人将把这些工作写入空间。一个或多个消费者将(在一个事务下)取出作业并并行处理,然后将结果写回。由于它处于事务下,因此如果出现问题,该作业将再次提供给另一个消费者。

    您可以通过添加更多消费者来轻松地扩展此功能。当消费者是独立的虚拟机并且您可以跨网络进行扩展时,这种方法尤其有效。

        5
  •  0
  •   Paul McKenzie    16 年前

    如果您对Spring的想法持开放态度,那么请查看他们的Spring集成项目。它提供了所有开箱即用的队列/线程池样板,让您专注于业务逻辑。使用@annotations将配置保持在最低限度。

    顺便说一句,Goetz非常好。

        6
  •  0
  •   Ben Manes    16 年前

    这听起来不像是一个主工问题,而是一个线程池之上的专用客户机。考虑到您有很多清理线程,而没有太多的处理单元,简单地执行一个清理过程,然后再执行一个计算过程可能是值得的。通过将工作项存储在集合中,唯一性约束将删除重复项。第二个过程可以将所有工作提交给ExecutorService以并行执行该过程。

    主-辅模型通常假定数据提供者拥有所有工作,并将其提供给主模型进行管理。主节点控制工作执行,并处理分布式计算、超时、故障、重试等。fork-join抽象是一个递归的而不是迭代的数据提供者。map-reduce抽象是一个多步骤的主工作程序,在某些场景中非常有用。

    大师级工作者的一个很好的例子是处理琐碎的并行问题,比如寻找素数。另一种是数据加载,其中每个条目都是独立的(验证、转换、阶段)。处理已知工作集、处理故障等的需要使主工作模型不同于线程池。这就是为什么主机必须控制并将工作单元推出,而线程池允许工作人员从共享队列中提取工作。