【问题标题】:How to correctly shutdown ScheduledExecutorService如何正确关闭 ScheduledExecutorService
【发布时间】:2021-12-28 08:11:02
【问题描述】:

我仍在努力了解如何使用 ScheduledExecutorService。我想要一个 ScheduledExecutorService/ScheduledThreadPoolExecutor,它可以调度多个任务并让它们运行固定的次数。一旦不再安排任务,我想关闭 ScheduledThreadPoolExecutor。我试图通过编写一些测试来了解这如何工作。我尝试的第一件事是:

    @Test
    public void testExecutorService() throws Exception {
        ScheduledThreadPoolExecutor executorService = new ScheduledThreadPoolExecutor(1);
        executorService.setRemoveOnCancelPolicy(true);
        CountDownLatch latchOne = new CountDownLatch(5);
        CountDownLatch latchTwo = new CountDownLatch(10);

        ScheduledFuture<?> scheduledFutureOne = executorService.scheduleAtFixedRate(new MyRunnerOne(latchOne), 0, 1, TimeUnit.SECONDS);
        ScheduledFuture<?> scheduledFutureTwo = executorService.scheduleAtFixedRate(new MyRunnerTwo(latchTwo), 0, 2, TimeUnit.SECONDS);
        
        latchOne.await();
        scheduledFutureOne.cancel(false);
        latchTwo.await();
        scheduledFutureTwo.cancel(false);
        
        System.out.println("Shutting down service...");
        executorService.shutdown();
        Thread.sleep(100);
        assertTrue(executorService.isTerminated());
    }

    class MyRunnerOne implements Runnable
    {
        CountDownLatch latch;

        MyRunnerOne(CountDownLatch latch)
        {
            this.latch = latch;
        }

        @Override
        public void run()
        {
            System.out.println("Do Task One : " + latch.getCount());
            latch.countDown();
        }
    }


    class MyRunnerTwo implements Runnable
    {
        CountDownLatch latch;

        MyRunnerTwo(CountDownLatch latch)
        {
            this.latch = latch;
        }

        @Override
        public void run()
        {
            System.out.println("Do Task Two: " + latch.getCount());
            latch.countDown();
        }
    }

这很好,我得到了预期的输出:

Do Task One : 5
Do Task Two: 10
Do Task One : 4
Do Task One : 3
Do Task Two: 9
Do Task One : 2
Do Task One : 1
Do Task Two: 8
Do Task Two: 7
Do Task Two: 6
Do Task Two: 5
Do Task Two: 4
Do Task Two: 3
Do Task Two: 2
Do Task Two: 1
Shutting down service...

但是,我这只是因为我在等待latchTwo 之前等待latchOne,因为第一个任务花费的时间更少。如果我切换这个顺序,即

        latchTwo.await();
        scheduledFutureTwo.cancel(false);
        latchOne.await();
        scheduledFutureOne.cancel(false);

任务一将被进一步执行,即使锁存器已达到零,因为它仅在任务二之后被取消。有没有办法在锁存器为零时立即执行某些操作(在这种情况下为 scheduledFutureOne.cancel(false))并同时对多个锁存器执行此操作?

我还尝试按照this answer 中描述的设置再次运行良好,但我不知道在哪里/如何关闭 ExecutorService。设置类似,增加一个原子整数,直到达到所需的调用次数,然后我们取消未来。不过,这仍然不会关闭 ExecutorService。

TLDR 我想用一个 ExecutorService 安排多个任务,每个任务都有固定数量的调用。 ExecutorService 处理任务的顺序应该允许是随机的。一旦不再安排任何任务,ExecutorService 就应该关闭。

谢谢你们的任何想法:)

【问题讨论】:

  • run() 方法内的一个简单的for 循环中安排一个完成五或十个任务的大任务是否是一种选择?是否可以立即将同一任务安排五次或十次?
  • 有什么特别的原因要关闭它吗? Executors.newCachedThreadPool() 不满足您的要求的任何特殊原因?
  • 您好,感谢您的回答。 @Progman 我不太喜欢第一个选项,因为这只是一个示例,它更像是 100000 个任务,然后 for 循环似乎有些笨拙。我看不出一次安排所有任务有什么问题,但还没有真正看到好处吗?
  • @Shark 老实说,我只是想关闭它,因为这似乎是正确的做法 - 如果我不再使用资源,请清理它。而且它会被多次调用,所以我不想在某个时候有很多空闲的 executorServices。我刚刚阅读了 newCachedThreadPool ,它看起来很不错,尽管有些人仍然说在这里也应该调用shutdown。我会再读一些,谢谢你的建议:)
  • 嗯....我不知道。让一个 ExecutorService 在应用程序启动时初始化并在应用程序停止时关闭(对我来说)似乎比初始化 ExecutorService、运行一批任务并关闭它——每次你想要执行一些任务。

标签: java executorservice scheduledexecutorservice


【解决方案1】:

如果你让任务在执行器服务中重新安排自己会更容易做到:

ScheduledThreadPoolExecutor executorService = new ScheduledThreadPoolExecutor(1);
CountDownLatch latchOne = new CountDownLatch(1);
CountDownLatch latchTwo = new CountDownLatch(1);

executorService.submit(new MyRunnerOne(executorService, latchOne, 5));
executorService.submit(new MyRunnerTwo(executorService, latchTwo, 10));

latchTwo.await();
latchOne.await();

System.out.println("Shutting down service...");
executorService.shutdown();
executorService.awaitTermination(100, TimeUnit.SECONDS);
System.out.println("Terminated: " + executorService.isTerminated());

//...

class MyRunnerOne implements Runnable {
    private final ExecutorService service;
    private final CountDownLatch latch;
    private final int count;

    MyRunnerOne(ExecutorService service, CountDownLatch latch, int count) {
        this.service = service;
        this.latch = latch;
        this.count = count;
    }

    @Override
    public void run() {
        System.out.println("Do Task One : " + count);
    
        if (count > 1) {
            service.submit(new MyRunnerOne(service, latch, count - 1));
        } else {
            latch.countDown();
        }
    }
}

Full code.

【讨论】:

  • 感谢您的建议,我将仔细研究此选项。似乎日程安排的一些不错的功能会丢失,但我可能可以解决这个问题:)
猜你喜欢
  • 2015-09-11
  • 2020-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-27
  • 2017-03-29
  • 2019-07-20
  • 2019-04-12
相关资源
最近更新 更多