【问题标题】:Check for thread shutdown and execute a task once it shutdown检查线程关闭并在关闭后执行任务
【发布时间】:2019-05-07 17:13:10
【问题描述】:

我正在创建一个具有 10 个线程的固定大小的线程池,如下所示:

ExecutorService executorService = Executors.newFixedThreadPool(10);

for (int i = 0; i < poolSize; ++i) {
   executorService.execute(factory.get());
}

现在,在产生 10 个线程之后,每个线程都在执行一个可运行的对象。这里的Runnable由factory.get()提供

现在,我想做一件事,即一旦线程关闭,即它完成了一项任务,它就会再次选择另一个可运行对象并开始运行它。因此,基本上检查所有 10 个线程的状态并在线程关闭时再次执行可运行。

我知道我可以做类似的事情,其中​​我可以调用 executorService shutdown() 方法来做到这一点:

   while (!executorService.isShutdown()) {
        try {
            executorService.execute(factory.get());
        } catch (RejectedExecutionException e) {
            if (!executorService.isShutdown())
                log.warn("task submission rejected");
        }
    }

但是在这种方法中,问题是我一直在调用执行函数,这会增加 LinkedBlockingQueue 并且不会达到我的目的。

如果我能以某种方式检查 Threads 状态,那就太好了,因为这样可以避免返回从 executor 服务获取更多工作的开销。

请建议我如何在代码中做到这一点。

【问题讨论】:

  • 几个建议:1)你可以把它翻转过来,减少线程的状态,而更多地关注 Runnable 的状态:你的 Runnable 派生类型中有什么可以订阅或要求在工作完成时通知您? 2)考虑使用Callable而不是Runnable;这样你就可以询问返回的 Future isDone() 以确定任务是否完成。
  • would increase LinkedBlockingQueue and won't serve my purpose 你想达到什么目的?如果您在将任务提交给执行程序之前就阻塞了,那么您并没有真正正确地利用线程池。队列缓冲任务有什么问题?
  • 完成任务不应该关闭线程,这违背了拥有线程池的目的。不清楚你想要完成什么,但我怀疑你应该让执行者做它的事情而不是微观管理它。

标签: java multithreading


【解决方案1】:

您可以使用有界执行器。以下是 code 来自 jcip 的 Brian Goetz

/**
 * BoundedExecutor
 * <p/>
 * Using a Semaphore to throttle task submission
 *
 * @author Brian Goetz and Tim Peierls
 */
public class BoundedExecutor {
    private final Executor exec;
    private final Semaphore semaphore;

    public BoundedExecutor(Executor exec, int bound) {
        this.exec = exec;
        this.semaphore = new Semaphore(bound);
    }

    public void submitTask(final Runnable command)
            throws InterruptedException {
        semaphore.acquire();
        try {
            exec.execute(new Runnable() {
                public void run() {
                    try {
                        command.run();
                    } finally {
                        semaphore.release();
                    }
                }
            });
        } catch (RejectedExecutionException e) {
            semaphore.release();
        }
    }
}

然后创建并使用它:

ExecutorService executorService = Executors.newFixedThreadPool(10);
BoundedExecutor boundedExecutor = new BoundedExecutor(executorService,10);

while (!executorService.isShutdown()) {
    try {
        boundedExecutor.submitTask(factory.get());
    } catch (InterruptedException e) {
    }
}       

这样你将始终有 10 个线程在运行,新任务只有在旧任务完成后才会提交,你可以通过关闭执行器来停止执行。

【讨论】:

    【解决方案2】:

    如果要连续运行 10 个线程,为什么要使用线程池?只需自己启动 10 个线程即可。

    为什么在 10 个工作线程之上有一个“控制器”线程?只需让每个线程请求执行下一个任务即可。

    以下假设factory.get() 是线程安全的。

    for (int i = 0; i < 10; i++) {
        new Thread(() -> {
            for (;;) {
                Runnable r = factory.get(); // <-- must be thread-safe
                if (r == null)
                    break; // stop thread if no more tasks to execute
                try {
                    r.run();
                } catch (Exception e) {
                    // log exception here
                }
            }
        }).start();
    }
    

    如果需要,将 10 个线程对象保存在一个数组中。

    如果您希望能够在不让factory.get() 返回 null 的情况下关闭线程,请添加一个 volatile 布尔值,并在循环中检查它。

    【讨论】:

    • 在这种情况下,我们将如何处理线程被杀死的情况?
    • 我的意思是你如何处理中断的情况?如果其中一个线程被中断怎么办。在那种情况下,我们想杀死所有线程,对吧?
    • @hatella 一个线程只有在你决定中断它时才会被中断。如果您希望中断将其关闭,那么您可以添加逻辑来实现这一点。这是你的代码,你决定它应该做什么。我只是举了一个例子让你走上正轨。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-26
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 2021-05-22
    • 2016-06-24
    • 1970-01-01
    相关资源
    最近更新 更多