ExecutorService:更好的线程流量调节

时间:2017-10-22 09:26:13

标签: java multithreading executorservice executor

我有一个基于ExecutorService Thread流程规则的问题: 我想.submit()执行多个线程,我想要一些线程等到特定的前一个线程完成执行。

到目前为止,我通过使用CountDownLatch()知道了一种这样的方法。以下示例说明了需要在第三个线程开始之前完成的2个线程:

public class Example{

public static void main(String[] args) throws InterruptedException {

    CountDownLatch cdl = new CountDownLatch(2); //count of 2
    ExecutorService executor = Executors.newCachedThreadPool();
    executor.submit(new Runnable() {
        public void run() {
             method1(); //simulation of a useful method 1
            cdl.countDown(); //reduces the count by one
        }
    });
    executor.submit(new Runnable() {
        public void run() {
             method2(); //simulation of a useful method 2
            cdl.countDown();
        }
    });
    cdl.await(); //will wait until both methods are complete
    executor.submit(new Runnable() { 
        public void run() {
        result(); //simulation of a method that needs previous threads to execute }
        });
    }
}

很明显,这样的方法对于这样的工作来说不是最佳的,因为一方面,不能在.await()之后添加不依赖于CDL本身的额外线程。

因此,使用ExecutorService来管理Thread流程的更好的替代方法是什么,与CDL相比,它允许更好地操作Thread执行。

3 个答案:

答案 0 :(得分:3)

在第3 CountDownLatch

内等待Runnable
executor.submit(new Runnable() { 
    public void run() {
       cdl.await(); //will wait until both methods are complete
       result(); //simulation of a method that needs previous threads to execute }
    });
}

您可能需要将cdl声明为final,以便在匿名Runnable实例中引用它:

final CountDownLatch cdl = new CountDownLatch(2); //count of 2 

替代方法

有一个方法可以创建第三个任务:

void createThirdTask() {
    executor.submit(new Runnable() { 
        public void run() {
           result(); //simulation of a method that needs previous threads to execute }
        });
    }
}

在前两个任务和计数器之间有一个共享锁。

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private int count = 2;

CDL内部method1()method2()递减值,如果达到零,则触发第三项任务。

void method1() {
        //your actual code goes here
    try {
        lock.writeLock().lock();
        if(count-- == 0) {
            createThirdTask();
        }
    } finally {
        lock.writeLock().unlock()
    }
}

ReentrantReadWriteLock可以防止竞争条件。

答案 1 :(得分:3)

我建议将CompletableFuture用于此类线程任务。它可能看起来像这样:

public class RunAfterNThreads {

public static void main(String[] args) throws ExecutionException, InterruptedException {
    CompletableFuture.supplyAsync(RunAfterNThreads::runFirstBatchOfThreads)
            .thenAcceptAsync((t) -> runSecondBatchOfThreads(null)).get();
}

private static Object runSecondBatchOfThreads(Object something) {
    return something;
}

private static <U> U runFirstBatchOfThreads() {
    return null;
}

}

如果你想使用一些库,我会建议探索类似Akka(事件驱动)的东西。

答案 2 :(得分:2)

我不确定Java是否真的有适当的标准机制来帮助您实现这一目标。 Eclipse的内部代码有很多额外的类来处理任务及其依赖项。

但是,我认为你或许可以使用&#34; CompletableFuture这样做:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Runnable r1 = () -> System.out.println("runnable 1");
        Runnable r2 = () -> System.out.println("runnable 2");
        Runnable r3 = () -> System.out.println("runnable 3");


        CompletableFuture<Void> cf1 = CompletableFuture.runAsync(r1, executor);
        CompletableFuture<Void> cf2 = CompletableFuture.runAsync(r2, executor);

        cf1.runAfterBoth(cf2, r3);
  }
}

如果您的情况更复杂,那么最好搜索“定向非循环图”&#39;任务库。