【问题标题】:Reusing ThreadPoolExecutor vs Creating and Disposing Ad Hoc?重用 ThreadPoolExecutor 与创建和处理 Ad Hoc?
【发布时间】:2014-10-06 07:17:27
【问题描述】:

我正在构建一个具有几个阶段的多线程进程,每个阶段迭代未知数量的对象(来自缓冲查询结果集或文本文件的数十万个对象)。每个阶段将为每个对象启动一个可运行或可调用对象,但所有可运行/可调用对象必须完成才能进入下一个阶段。

我不想使用锁存器或任何类型的同步器,因为我不想损害吞吐量。我怀疑闩锁的内部结构会通过同步计数器减慢速度。我也不想使用带有 invokeAll() 的期货列表,因为我想在迭代它们时立即开始执行可运行对象。

但是,为每个阶段创建一个 ThreadPoolExecutor,循环并提交所有可运行文件,然后为每个阶段关闭它似乎是一个功能解决方案......

public void runProcess() {

ResultSet rs = someDbConnection.executeQuery(someSQL);

ExecutorService stage1Executor = Executors.newFixedThreadPool(9);
while (rs.next()) { 
//SUBMIT UNKNOWN # OF RUNNABLES FOR STAGE 1
}
rs.close();
stage1Executor.shutdown(); 

rs = someDbConnection.executeQuery(moreSQL);

ExecutorService stage2Executor = Executors.newFixedThreadPool(9);
while (rs.next()) {  
//SUBMIT UNKNOWN # OF RUNNABLES FOR STAGE 2
}
rs.close();
stage2Executor.shutdown();

}

但是,我知道设置线程、线程池以及任何涉及并发的东西的构建和销毁都是昂贵的。或者也许这没什么大不了的,我只是对性能过于谨慎,因为无论如何并发都会产生昂贵的开销。有没有更有效的方法来做到这一点?使用某种我不知道的等待完成操作?

【问题讨论】:

    标签: java multithreading concurrency threadpoolexecutor


    【解决方案1】:

    如果您销毁线程池并重新初始化一个新线程池,则可能会比使用 CountDownLatch 花费更多!

    此外,调用stage1Executor.shutdown(); 并不能保证所有当前线程都会在新的 ExecutorService 启动并运行之前完成它们的执行。即使拨打shutdownNow()也不能保证! (你可能不想调用shutdownNow(),因为你希望你的线程完成执行)。

    Donald Knuth 曾经说过:

    过早的优化是万恶之源。

    所以即使你没有被我说服——最好听他的:)

    【讨论】:

    • 因此,如果我从相同数量的对象(我提前计算)中创建 201,331 个可运行对象,则使用该数字创建一个倒计时锁,并让每个可运行对象减少倒计时锁...不会countdownlatch 由于其内部同步而扼杀并发?我猜它有一个原子整数或内部可能会限制吞吐量的东西。我得到了过早的优化等等,但是这个过程目前是单线程的,需要性能优化。
    • @ThomasN。如果您正在创建 200K 线程,那么您做错了:infoq.com/articles/Java-Thread-Pool-Performance-Tuning
    • 我不是在创建 200K 线程,而是在创建 200K 可运行的队列以在 9 个线程上执行。我从并发研究(Goetz 的“实践中的并发”)中得到的印象是并发迭代应该是这样完成的。
    • @ThomasN。太棒了 - 这确实是这样做的方法!让他们在开始任务 #2 之前完成第一个任务的方法是使用倒计时锁(而不是尝试自己实现)。
    • @Thomas N.:不要考虑锁存器中的原子整数,我向你保证,每个线程池执行器实现都会有至少一个原子变量也为每个任务操作一次。如果你真的认为一个原子更新的成本比你的可运行文件应该做的高,那么你的操作过于分散了。在不同的并发工具之间切换永远不会解决这样的问题。
    【解决方案2】:

    设置和拆除少量线程池可以忽略不计。在测试中循环尝试一下。

    使用倒计时闩锁很好,但这可能只是重复了 ThreadPoolExecutor 在内部所做的工作,并且您的任务与您的执行框架结合起来。不喜欢这种方法。

    至于原始代码,ExecutorService 有一个awaitTermination 方法,因此您可以等到工作完成后再进入下一个阶段。

    为了我的钱,你的伪代码很好。只需将 executor.shutdown() 替换为 shutdownAndAwaitTermination(ExecutorService),其来源在这里:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html

    【讨论】:

    • 我可能会采用这种方法,因为我不想提前知道计数。不过,我愿意支付入场费看你和阿法辛决一胜负。
    猜你喜欢
    • 1970-01-01
    • 2012-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多