Java 如何在ThreadPoolExecutor中的线程之间分配任务

Java 如何在ThreadPoolExecutor中的线程之间分配任务,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有以下问题 我有一个任务队列,有很多类型的任务,如: A, B, C, D, ... 我在线程池中执行这些任务 但我必须同时限制同一类型任务的执行,因此,这是不好的: Thread-1: [A, D, C, B, ...] Thread-2: [A, C, D, B, ...] 类型A和B的任务可以同时执行 但这是好的: Thread-1: [A,B,A,B,...] Thread-2: [C,D,D,C,...] 因此,相同类型的任务总是按顺序执行 实现此功能的最简单方法是什么 Com

我有以下问题

我有一个任务队列,有很多类型的任务,如:

A, B, C, D, ...
我在线程池中执行这些任务

但我必须同时限制同一类型任务的执行,因此,这是不好的:

Thread-1: [A, D, C, B, ...]
Thread-2: [A, C, D, B, ...]
类型A和B的任务可以同时执行

但这是好的:

Thread-1: [A,B,A,B,...]
Thread-2: [C,D,D,C,...]
因此,相同类型的任务总是按顺序执行

实现此功能的最简单方法是什么

CompletableFuture.supplyAsync(this::doTaskA)  
                 .thenAccept(this::useResultFromTaskAinTaskB);
上面发生的事情是,任务A和相关任务B实际上是在同一个线程中运行的(一个接一个,不需要“获取”新线程来开始运行任务B)

或者,如果您不需要任务A中的任何信息,但需要在运行任务B之前等待任务完成,则可以对任务A使用
runAsync


默认情况下,CompletableFuture将使用公共线程池,但如果您希望对使用哪个线程池进行更多控制,可以使用自己的执行器将第二个参数传递给使用自己线程池的
async
方法。

使用Akka这样的actor框架可以轻松解决此问题

对于每种类型的任务。创造一个演员

对于每个单独的任务,创建一条消息并将其发送给相应类型的参与者。消息的类型可以是
Runnable
,就像现在一样,参与者的反应方法可以是

@凌驾
公共void onReceive(对象消息){
((Runnable)msg.run();
}


这样,您的程序就可以对任意数量的线程正确运行。

我认为您可以实现自己的DistributedThreadPool来控制线程。它就像某种主题订阅者/发布者结构

我做了如下示例:

class DistributeThreadPool {

Map<String, TypeThread> TypeCenter = new HashMap<String, TypeThread>();

public void execute(Worker command) {
    TypeCenter.get(command.type).accept(command);
}

class TypeThread implements Runnable{

    Thread t = null;
    LinkedBlockingDeque<Runnable> lbq = null;

    public TypeThread() {
        lbq = new LinkedBlockingDeque<Runnable>();
    }

    public void accept(Runnable inRun) {
        lbq.add(inRun);
    }

    public void start() {
        t = new Thread(this);
        t.start();
    }


    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                lbq.take().run();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public DistributeThreadPool(String[] Types) {
    for (String t : Types) {
        TypeThread thread = new TypeThread();
        TypeCenter.put(t, thread);
        thread.start();
    }
}

public static void main(String [] args) {
        DistributeThreadPool dtp = new DistributeThreadPool(new String[] {"AB","CD"});

        Worker w1 = new Worker("AB",()->System.out.println(Thread.currentThread().getName() +"AB"));
        Worker w2 = new Worker("AB",()->System.out.println(Thread.currentThread().getName() +"AB"));
        Worker w3 = new Worker("CD",()->System.out.println(Thread.currentThread().getName() +"CD"));
        Worker w4 = new Worker("CD",()->System.out.println(Thread.currentThread().getName() +"CD"));
        Worker w5 = new Worker("CD",()->System.out.println(Thread.currentThread().getName() +"CD"));

        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);
        workers.add(w4);
        workers.add(w5);

        workers.forEach(e->dtp.execute(e));
    }
}
类DistributeThreadPool{
Map TypeCenter=newhashmap();
公共void执行(Worker命令){
TypeCenter.get(command.type).accept(command);
}
类TypeThread实现Runnable{
线程t=null;
LinkedBlockingDeque lbq=null;
公共类型线程(){
lbq=新的LinkedBlockingDeque();
}
公共无效接受(可在运行中运行){
lbq.add(inRun);
}
公开作废开始(){
t=新螺纹(本螺纹);
t、 start();
}
@凌驾
公开募捐{
而(!Thread.interrupted()){
试一试{
lbq.take().run();
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}
}
公共DistributeThreadPool(字符串[]类型){
for(字符串t:类型){
TypeThread线程=新的TypeThread();
类型中心。放置(t,螺纹);
thread.start();
}
}
公共静态void main(字符串[]args){
DistributeThreadPool dtp=新的DistributeThreadPool(新字符串[]{“AB”,“CD”});
Worker w1=新的Worker(“AB”,()->System.out.println(Thread.currentThread().getName()+“AB”);
Worker w2=新的Worker(“AB”,()->System.out.println(Thread.currentThread().getName()+“AB”);
Worker w3=new Worker(“CD”,()->System.out.println(Thread.currentThread().getName()+“CD”);
Worker w4=new Worker(“CD”,()->System.out.println(Thread.currentThread().getName()+“CD”);
Worker w5=new Worker(“CD”,()->System.out.println(Thread.currentThread().getName()+“CD”);
List workers=new ArrayList();
工人。添加(w1);
增加(w2);
工人。添加(w3);
添加(w4);
增加(w5);
workers.forEach(e->dtp.execute(e));
}
}

有趣的问题。我想到两个问题:

有多少种不同类型的任务?

如果有相对较少的线程,最简单的方法可能是为每种类型创建一个线程,并将每个传入任务分配给其类型的线程。只要任务在类型之间保持平衡(这是一个很大的假设),利用率就足够了

任务完成的预期及时性/延迟是多少?

如果您的问题在时效性上是灵活的,您可以按计数或时间间隔批处理每种类型的传入任务,将退役的每批任务提交到池中,然后等待批处理完成后再提交另一批同类任务

您可以将第二个备选方案调整为小到一个的批量大小,在这种情况下,等待完成的机制对于效率非常重要
CompletableFuture
符合这里的要求;您可以使用
thenRunAsync
将“轮询A类型的下一个任务并提交到池”操作链接到该任务,然后启动并忘记该任务

您必须为每个任务类型维护一个外部任务队列;FJ池的工作队列将仅用于正在进行的任务。尽管如此,这种设计仍然有很好的机会合理地处理每种类型的任务计数和工作负载的不平衡


希望这有帮助

实现密钥有序执行器。每个任务都应该有一个密钥。具有相同密钥的任务将排队并依次执行,具有不同密钥的任务将并行执行


您可以尝试自己制作,但这很棘手,而且容易出错。我看不出答案中有什么错误。

你们的问题很模糊,我不确定我是否理解。但是你可以考虑只使用2个单独的线程池。@杰米,这是一个非常笨拙的解决方案。这也可以吗?线程-1[A1,A2,B1,B2]?@john16384,是的。如果B1需要A1的一些东西,并且基本上在等待A1完成,那么您可以使用CompletableFuture的CompletionStages来完成这项工作。。。我可以举一个例子。是的,Akka可以很容易地解决这个问题,但是我认为集成Akka来解决这个问题会带来很大的开销。如果有1000种类型的任务呢?你的意思是希望同时按顺序运行1000种类型的任务吗@安东