Java中的并行处理; 需要建议,即在Runnanble / Callable接口上

假设我有一组需要以两种不同方式进行分析的对象,这两种方式都需要相对较长的时间并涉及IO调用,我试图弄清楚如何/如果我可以优化我的软件的这一部分,特别是使用多个处理器(我所坐的机器是一个8核的i7,在执行过程中几乎不会超过10%的负载)。

我对并行编程或multithreading很不熟悉(不确定正确的术语是什么),所以我已经阅读了一些先前的问题,特别是关注高度投票和信息丰富的答案。 我也正在阅读有关并发的Oracle / Sun教程 。

这是我到目前为止的想法;

  • 线程安全的集合包含要分析的对象
  • 只要集合中有对象(它们从一系列查询中一次出现一对),就会启动每个对象的一个​​线程
  • 每个特定的线程负责初始的预分析准备工作; 然后调用分析。
  • 这两个分析实现为Runnables / Callables,因此必要时由线程调用。

我的问题是:

  1. 这是一个合理的计划,如果没有,你会怎么做呢?
  2. 为了确保事情不会失控,我应该实现一个ThreadManager还是那种启动和停止线程的东西,并在它们完成时重新分配它们? 例如,如果我要分析256个对象,并且总共有16个线程,则ThreadManager将第一个完成的线程分配给要分析的第17个对象等。
  3. 除了Callable可以返回结果的事实之外,Runnable / Callable之间是否存在显着差异? 否则我应该尝试实现自己的界面,在这种情况下为什么?

谢谢,

  1. 您可以使用BlockingQueue实现来保存对象并从那里生成线程。 该界面基于生产者 – 消费者原则。 如果队列已满, put()方法将阻塞,直到有更多空间,如果队列为空,则take()方法将阻塞,直到队列中再次存在某些对象为止。

  2. ExecutorService可以帮助您管理线程池。

  3. 如果您正在等待生成的线程的结果,那么Callable接口是一个好主意,因为您可以提前开始计算并在您的代码中使用Future -s中的结果。 至于与Runnable接口的差异,来自Callable javadoc :

    Callable接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类而设计的。 但是,Runnable不会返回结果,也不能抛出已检查的exception。

在寻求Java并发时需要考虑的一些常规事项:

  • 事实并非来自事实。 volatile , AtomicReference和java.util.concurrent.atomic包中的其他对象是你的朋友。
  • 您需要使用同步和锁定仔细确保复合操作的primefaces性

你的想法基本上是合理的。 但是,不是直接创建线程,也不是通过您自己设计的某种ThreadManager间接创建线程,而是使用Java的并发包中的Executor。 它可以满足您的所有需求,其他人已经花时间编写和调试它。 执行程序管理任务队列,因此您无需担心自己提供线程安全队列。

除了前者返回一个值之外,Callable和Runnable之间没有区别。 执行者将处理两者,并为它们做好准备。

我不清楚您是否计划将准备步骤作为分析的单独任务,或将其折叠成其中一个,该任务将在中途产生另一个分析任务。 我想不出任何理由强烈偏好一个到另一个,但这是你应该考虑的选择。

Executors提供了用于创建线程池的工厂方法。 具体来说,执行程序#newFixedThreadPool(int nThreads)创建一个使用无界队列的固定大小的线程池。 此外,如果线程因故障而终止,则新线程将被替换为其位置。 因此,在你要调用的256个任务和16个线程的具体示例中

  // create pool ExecutorService threadPool = Executors.newFixedThreadPool(16); // submit task. Runnable task = new Runnable(){};; threadPool.submit(task); 

重要的问题是确定线程池的正确线程数。 看看这是否有助于高效的线程数

听起来很合理,但实施起来并不像看起来那么简单。 也许你应该检查jsr166y项目。 这可能是解决您问题的最简单方法。