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

时间:2011-02-09 10:14:43

标签: java multithreading concurrency

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

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

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

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

我的问题是:

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

4 个答案:

答案 0 :(得分:3)

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

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

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

答案 1 :(得分:3)

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

    < / LI>
  2. ExecutorService可以帮助您管理线程池。

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

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

  4. 在寻求Java并发时需要考虑的一些常见问题:

    • 可见性不是来自事实。 volatile AtomicReference以及 java.util.concurrent.atomic 包中的其他对象都是您的朋友。
    • 您需要使用同步和锁定小心确保复合操作的原子性

答案 2 :(得分:2)

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

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

重要的问题是确定线程池的正确线程数。看看这有助于Efficient Number of Threads

答案 3 :(得分:0)

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