gpt4 book ai didi

java - 使用ExecutorService确定性地将任务分配给线程

转载 作者:塔克拉玛干 更新时间:2023-11-02 19:23:50 26 4
gpt4 key购买 nike

给定具有固定线程池的Executor服务,是否可以保证将任务确定性地分配给线程?更准确地说,假设只有两个线程,即pool-thread-0和pool-thread-1,并且有两个要执行的任务的集合。我希望实现的是,前一个线程始终执行第一个线程,而后者则处理其余的线程。

这是一个例子:

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始终执行第一个任务。任何帮助将不胜感激!

最佳答案

ExecutorService并非旨在为其Callable/Runnable提供“线程关联性”。有人可能会说“这很重要”,那里的API是让程序员处理工作描述(可调用的),而不是处理线程。

您的设计“由于每个线程都有一个随机数据生成器”不适合ExecutorService,原因有以下三个:

  • 您无法控制将创建(或销毁)哪些线程以及何时创建线程(如果发生崩溃,池将重新创建该线程,但是它将获得什么样的随机生成器?)。因此,我们无法推断出可靠的方式来表示“此线程”具有“此生成器”,更不用说“第二”线程具有“此生成器”,因为甚至可能没有第二线程(如果每个任务是如此之快,以至于它们的处理速度比您调度它们的速度快?
  • 您无法控制何时执行什么任务。好吧...使用Executors.newFixedThreadPool,您可以按提交顺序进行分发,这是您的事,但就您所知,OS调度程序可以将线程1的优先级全部赋予,最终将完成所有工作,并且线程2根本什么也没做(在两者之间可以是任何比例)。
  • 可以将“数据生成器”传递给线程的唯一方法是,如果您覆盖执行程序服务的ThreadFactory。否则,您将无法访问线程实例(运行时可调用它们自己的appart)。因此,要将特定的生成器与特定的线程相关联,您必须知道当前正在创建的线程号,如果要计算线程数,这很容易,但是如果您想知道什么是Callable,则很难。线程旨在(请参见第2点)。

  • 因此,我强烈建议您定义将工作单元与数据生成器关联的其他方式,因为“线程实例”通常是不可靠的-至少不是通过Executor Service。
    例如。当你说

    I need to provide that the combinations of threads and data they process are repeatable.



    我知道您将始终调度一定数量的Callable,并且您需要它们中的每一个都处理特定生成器发出的特定数据集。假设我们有给定数量的任务和3个生成器,task(N)将使用生成器 N%3

    为了使结果可重复,您还需要使使用同一生成器的任务不能同时执行(要通过线程亲和力实现的目标?)。

    有一定数量的模式可以实现这一目标。

    1是:重构为生产者/消费者(反之亦然)

    在您的执行程序服务中执行3个任务,每个任务都监听 BlockingQueue(其私有(private)等待列表),并具有自己的私有(private)生成器。这些是消费者。
    让您的主线程成为生产者:当它创建工作单元(在您的原始设计中曾经是 Callable)时,编号为N,将其分配给等待队列编号N%3。就是这样:每个消费者将按您希望的顺序依次接收自己的数据进行计算。您已经达到“亲和力”。

    二是:让任务自己分派(dispatch)任务。 (以骇人的方式来做)

    首先,重构您的可调用对象以链接到他们需要使用的生成器。
    然后,在您的主线程上,构建要为每个生成器运行的任务列表。
    从主线程调度每个生成器的第一个任务。
    在每个可调用对象的末尾,使可调用对象调度其列表中的下一个工作单元。
    但是要小心不要“锁定您”,如果您从可调用对象中调度可调用对象,请不要等待结果,因为这将阻止可调用对象完成,从而阻止新调度的对象执行。这是一个僵局。

    3是:与2相同,效率较低,但风险较小

    与其从内部可调用对象中调度可调用对象,不如通过等待 future 仅从您的主线程中调度。

    通过这两种方式中的任何一种,都不能保证首先完成或最后完成哪些任务,但是可以保证您调度的工作单元可以预见地与您控制的数据生成器相关联,并且可以按照调度的顺序执行他们。希望这足够了。

    关于java - 使用ExecutorService确定性地将任务分配给线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25048385/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com