给定具有固定线程池的Executor服务,是否可以保证线程的确定性任务分配?更确切地说,假设只有两个线程,即pool-thread-0和pool-thread-1,并且有一组要执行的2个任务。我希望实现的是前一个线程总是执行第一个,而后者处理剩下的一个。
以下是一个例子:
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = newFixedThreadPool(2,
new ThreadFactoryBuilder().setNameFormat("pool-thread-%d").build());
for (int i = 0; i < 5; i++) {
List<Callable<Integer>> callables = ImmutableList.of(createCallable(1), createCallable(2));
executorService.invokeAll(callables);
}
}
public static Callable<Integer> createCallable(final int task) {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
currentThread().sleep(1000);
System.out.println(Thread.currentThread().getName() + " executes task num: " + task);
return task;
}
};
}
我机器的示例输出:
pool-thread-0 executes task num: 1
pool-thread-1 executes task num: 2
pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1
pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1
pool-thread-0 executes task num: 2
pool-thread-1 executes task num: 1
pool-thread-0 executes task num: 1
pool-thread-1 executes task num: 2
简而言之,我希望确保pool-thread-0始终执行第一个任务。任何帮助将不胜感激!
答案 0 :(得分:2)
ExecutorService并非旨在提供&#34;线程关联性&#34;到它的Callable / Runnable。有人可能会争辩说,这就是问题所在......,API允许程序员处理工作描述(Callable),而不是线程处理。
您的设计,其中&#34;有一个与每个线程关联的随机数据生成器&#34;由于以下三个原因,我不适合ExecutorService:
您无法控制将创建(或销毁!)的线程以及何时(如果一个崩溃了?该池将重新创建它,但它将获得什么随机生成器?)。所以我们无法推断出一种可靠的方式来说明这个线索&#34;有&#34;这个发电机&#34;,更不用说#34;第二个&#34;线程有&#34;这个发生器&#34;因为甚至可能没有第二个线程(如果每个任务都如此之快以至于它们的处理速度比你发送它们的速度要快呢?)。
您无法控制何时执行的任务。好吧......使用Executors.newFixedThreadPool,你可以按照提交的顺序调度它们,但是据你所知,操作系统调度程序可能会优先考虑线程1,最终会完成所有的工作,并且线程2根本不做任何事情(它可以是任何比例)。
唯一可以传递&#34;数据生成器的方法&#34;如果您覆盖执行程序服务的ThreadFactory,则为一个线程。否则,您无法访问线程实例(运行时来自可调用的appart)。因此,要将特定生成器与特定线程相关联,您必须知道您当前正在创建哪个线程编号,如果您正在计算线程,这很容易,但如果您正在尝试了解,则很难该线程的目的是什么?(参见第2点)。
所以我强烈建议您定义一些将工作单元与数据生成器关联起来的其他方法,因为&#34;线程实例&#34;一般来说不可靠 - 至少不是通过Executor Service。 例如。当你说
我需要提供他们处理的线程和数据的组合是可重复的。
据我所知,您将始终发送一定数量的Callables,并且您需要每个Callables处理由特定生成器发出的特定数据集。假如我们有一定数量的任务和3个生成器,任务(N)将使用生成器N%3
。
为了使结果可重复,您还需要同时执行使用相同生成器的任务(您希望通过线程关联实现什么?)。
有一定数量的模式可以实现这一目标。
在您的执行程序服务中创建3个任务,每个任务都侦听BlockingQueue
(其私有等待列表)并拥有自己的私有生成器。这些是消费者。
使您的主线程成为生产者:当它创建工作单位(原始设计中曾经是Callable
)数字N时,将其分配给等待队列号N%3。这就是它:每个消费者将按顺序依次接收自己的数据,按照您希望的顺序进行计算。你已经实现了&#34;亲和力&#34;。
首先,重构您的callables以获得他们需要使用的生成器的链接。 然后,在主线程上,构建要为每个生成器运行的任务列表 从主线程调度每台发电机的第一项任务 并且每个可调用的结束使Callable从其列表中调度下一个工作单元 尽管如此,如果你从callables中调用callables,请不要等待结果,因为这会阻止Callables完成,这反过来会阻止新调度执行。这是一个僵局。
<2> 3:与2相同,效率较低,但风险较小不是从callables内部发送Callables,而是通过等待期货从你的主线程发送。
执行这两种方法中的任何一种,您无法保证哪些任务将首先完成或最后完成,但您可以保证您发送的工作单元可预测地与您控制的数据生成器相关联,并且它们将在命令你派遣他们。希望这足够了。