【问题标题】:Is there an ExecutorService on blocking thread?阻塞线程上是否有 ExecutorService?
【发布时间】:2012-09-17 17:29:57
【问题描述】:

是否有ExecutorService 允许现有线程执行执行而不是产生新线程?如果它是 ScheduledExecutor 则奖励。大多数执行器会产生工作线程来执行,但我希望工作线程是我所在的现有线程。这是我想象的 API:

while (!executor.isTerminated()) {
    Runnable r = executor.take();
    r.run();
}

这类似于 SWT 和 JavaFX 允许主线程分派事件的方式,与 Swing 不同,后者需要产生自己的事件分派线程来处理事件。

动机:我目前有很多地方线程产生一个新的执行程序,然后调用 awaitTermination() 等待它完成。我想节省一些资源并防止堆栈跟踪被一分为二。

请注意,我不想要在 execute(Runnable) 的调用者线程中运行任务的执行程序,这是 this answer 和 Guava 的 MoreExecutors.sameThreadExecutor() 所做的。

【问题讨论】:

  • take() 如何表明假设的Executor#isTerminated() 谓词的答案在您看到它返回 true 后变为 false?
  • @seh 推测,isTerminated 读取了一些 volatile 变量,可以由另一个线程更改,要求它终止。毕竟这是一个关于并发的问题:3
  • 那个实现细节并没有解决我的设计问题。由于依赖 check-then-act 协议,循环被削弱了。 (SomeKindOf)Executor#isTerminated() 表示的真相可以在调用(SomeKindOf)Executor#take() 之前改变。那么take() 是返回null,还是抛出InterruptedException?它需要一个比 Runnable 更宽的共同域来表明它是在执行程序终止后调用的。
  • @seh 终止与关机不同。看ExecutorService。有两个值:shutdown(控制是否接受任务)和终止(如果还有作业要执行,则返回true。所以是的,一旦作业队列为空,关闭进程将中断其所有线程线程池和take 会抛出InterruptedException。添加isTerminated 检查将允许线程正常终止而不依赖异常。

标签: java concurrency executorservice


【解决方案1】:

java.util.concurrent 中的大多数执行程序的行为与您的预期完全一样。当任务过多时,有些会产生额外的线程,但通常可以将它们配置为设置限制。

要利用这种行为,不要每次都启动新的执行器 - 使用相同的执行器。要等待一组任务完成,请使用invokeAll(),或submit(),然后使用future.get()

【讨论】:

    【解决方案2】:

    我假设您想要控制新线程的创建,例如名称、守护进程状态等。使用ThreadFactory

    public class MyThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable runnable) {
            Thread t = new Thread(runnable, "MyThreadName");
            t.setDaemon(true);
            return t;
        }
    }
    

    这允许您控制线程创建,以便执行发生在制造的线程中,而不是来自默认ThreadFactory的某个默认线程。

    然后要使用它,Executors 中的所有方法都取一个ThreadFactory

    Executors.newExecutorOfSomeKind(new MyThreadFactory());
    

    编辑:我明白你的意思了。不幸的是,所有Executor 实现的行为(据我所知)都是创建新线程来运行任务,除了你提到的sameThreadExecutor。如果不通过 Thread 对象创建执行程序只是为了执行一项任务(这是一个可怕的设计 - 请参阅 cmets 了解我的意思),没有简单的方法可以完成您想要的。我建议更改代码以使用单个ExecutorExecutorCompletionService 之类的东西(请参阅this question)或使用分叉/连接模式。在 Java 7 中,fork/join 变得更容易(请参阅this Java trail)。对于 Java 7 之前的代码,请阅读 Java 中的计数 Semaphore(和 in general)。

    【讨论】:

    • 反对“糟糕的设计”。没有一个来自 java.util.concurrent 的 Executor 实现为每个任务创建新线程。
    • @Alexei Kaigorodov 请参阅缓存线程池 - docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/…。如果您有许多长时间运行的任务,则每个任务都有可能拥有自己的线程。
    • @John Vint 可能,但不是规则。如果可以的话,它会重用现有线程,也就是说,他们线程的 run() 方法确实包含任务队列上的循环,正如 TS 建议的那样。
    • 是的,但是说“没有一个来自 java.util.concurrent 的 Executor 实现为每个任务创建新线程”是一种误导,我知道 newCachedThreadPool 天真地使用已经完全杀死了系统。
    • @AlexeiKaigorodov @JohnVint 明确地说,通过“可怕的设计”,我的意思是让每个线程创建一个执行器来完成一组任务,然后再销毁该执行器是糟糕的设计。来自问题:“我目前有很多地方线程产生一个新的执行程序,然后调用 awaitTermination() 等待它完成。”据我了解,他有他的线程1)创建一个执行程序,2)提交作业,3)调用终止,4)调用awaitTerminate。他没有使用 ExecutorCompletionService 或 fork/join 之类的东西,而是在每次线程运行时分配一个执行程序。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多