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

时间:2017-01-19 14:52:24

标签: 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,...]

因此,相同类型的任务总是按顺序执行。

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

5 个答案:

答案 0 :(得分:1)

这个问题可以通过像Akka这样的演员框架轻松解决。

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

对于每个单独的任务,创建一条消息并将其发送给相应类型的actor。消息可以是Runnable类型,因为它们现在可能是,并且actor的反应方法可以是    @Override public void onReceive(Object msg) { ((Runnable)msg).run(); }

这样你的程序就可以在任意数量的线程上正确运行。

答案 1 :(得分:1)

我认为您可以实现自己的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));
    }
}

答案 2 :(得分:0)

CompletableFuture.supplyAsync(this::doTaskA)  
                 .thenAccept(this::useResultFromTaskAinTaskB);

上面发生的事情是,任务A和相关的任务B实际上是在同一个线程中运行的(一个接一个,不需要&#34;得到&#34;一个新线程开始运行任务B)。

如果您不需要任何信息,可以使用runAsync作为任务A,但在运行任务B之前需要等待它完成。

默认情况下,CompletableFuture将使用公共线程池,但如果您想要更多地控制使用哪个ThreadPool,您可以使用您自己的Executor将第二个参数传递给async方法你自己的ThreadPool。

答案 3 :(得分:0)

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

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

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

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

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

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

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

希望这会有所帮助。

答案 4 :(得分:0)

实施密钥有序执行程序。每项任务都应该有关键。具有相同键的任务将排队并将连续执行,具有不同键的任务将并行执行。

Implementation in netty

你可以尝试自己制作,但它很棘手且容易出错。我可以看到答案中有一些错误。