简体   繁体   English

如何在已关闭的ScheduledExecutorService中保持线程等待

[英]How to keep thread waiting in ScheduledExecutorService that has been shutdown

public ScheduledFuture<?> executeTaskWithDelay(String name, 
      final Runnable runnable, Period delay, boolean isDaemon) {
  ScheduledExecutorService executorService = 
        Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
          name, isDaemon));
  ScheduledFuture<?> future =  executorService.schedule(runnable, 
        delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS);
  executorService.shutdown(); 
  return future;
}

When I profiled the application, I noticed that the scheduled threads created by this method are always in "Running" rather than "Waiting" state before they get executed. 当我分析应用程序时,我注意到这个方法创建的调度线程在执行之前总是处于“正在运行”而不是“等待”状态。 If I remove executorService.shutdown() it does what I want (ie threads remain in waiting state until it is time for them to run). 如果我删除executorService.shutdown()它会做我想要的(即线程保持等待状态,直到它们运行的​​时间)。 However, without executorService.shutdown(), nonDaemon threads never get garbage-collected after execution. 但是,如果没有executorService.shutdown(),nonDaemon线程在执行后永远不会被垃圾收集。 Is there a way I can ensure threads are always in the waiting state before execution? 有没有办法可以确保线程在执行前始终处于等待状态? or what other replacement can I use for this method to ensure that: 或者我可以使用其他替代方法来确保:

  • I can attach prefix to the names of threads that run in the executor service (that's effectively what the DefaultThreadFactory implementation does) 我可以在执行程序服务中运行的线程的名称附加前缀(这实际上是DefaultThreadFactory实现的功能)
  • Non-daemon threads get GC after execution. 非守护程序线程在执行后获取GC。
  • created Threads remain in the waiting state until it is time for them to run. 创建的线程保持等待状态,直到它们运行为止。

The thread is not waiting because you didn´t call the future.get() method. 线程没有等待因为你没有调用future.get()方法。 I made a unit test to prove that. 我做了一个单元测试来证明这一点。

Test #1 (without calling future.get() method): 测试#1(不调用future.get()方法):

@Test
public void testSchedule() throws InterruptedException, ExecutionException {

    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    System.out.println(new Date());
    ScheduledFuture<?> future =  executorService.schedule(new Runnable() {

        public void run() {
            System.out.println(Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + " - Executing thread...");                
        }

    }, 5, TimeUnit.SECONDS);

    //System.out.println("future : " + future.get());

    executorService.shutdown();
    System.out.println(new Date());
}

And the output was: 输出是:

Thu May 24 10:11:14 BRT 2012
Thu May 24 10:11:14 BRT 2012

Test #2 (calling future.get() method): 测试#2(调用future.get()方法):

@Test
public void testSchedule() throws InterruptedException, ExecutionException {

    ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    System.out.println(new Date());
    ScheduledFuture<?> future =  executorService.schedule(new Runnable() {

        public void run() {
            System.out.println(Thread.currentThread().getId() + " - " + Thread.currentThread().getName() + " - Executing thread...");                
        }

    }, 5, TimeUnit.SECONDS);

    System.out.println("future : " + future.get());

    executorService.shutdown();
    System.out.println(new Date());
}

And the output was: 输出是:

Thu May 24 10:12:48 BRT 2012
8 - pool-1-thread-1 - Executing thread...
future : null
Thu May 24 10:12:53 BRT 2012

I hope it helps you! 我希望它对你有所帮助!

What you are seeing is the ScheduledExecutor delegating to the plain ThreadPoolExecutor for shutdown functionality. 您所看到的是ScheduledExecutor委托给普通的ThreadPoolExecutor以获取关闭功能。 When shutting down the TPE will have all its threads spin on the backing work queue until it is empty. 关闭TPE时,其所有线程都会在后备工作队列中旋转,直到它为空。 Well a ScheduledThreadPool uses a DelayedQueue which may not be empty but if you poll'd it you would get a null back because the task is not ready to schedule. 那么ScheduledThreadPool使用的DelayedQueue可能不是空的,但是如果你进行了轮询,你会得到一个空值,因为任务还没有准备好安排。 It will spin and spin until it is ready 它会旋转并旋转直到准备好

The only thing you can really do is shutdownNow and execute the tasks that returns some other way. 你唯一能做的就是shutdownNow并执行以其他方式返回的任务。

Also I have been told this is actually fixed in Java 7 我也被告知这实际上是在Java 7中修复的

Figured out a solution: It's shutdown() that changes the state of all pending task threads from 'waiting' to 'running'. 找出一个解决方案:它是shutdown(),它将所有待处理任务线程的状态从“等待”更改为“正在运行”。 Rather than calling shutdown() immediately, I now use the executorService to schedule the call to its own shutdown(). 我现在使用executorService来调度自己的shutdown(),而不是立即调用shutdown()。 This ensures that pending tasks remain in the waiting state for as long as possible - thereby saving CPU resource. 这可确保挂起的任务尽可能长时间保持等待状态 - 从而节省CPU资源。

public ScheduledFuture<?> executeTaskWithDelay(String name, 
      final Runnable runnable, Period delay, boolean isDaemon) {
  final ScheduledExecutorService executorService =  
        Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory(
      name, isDaemon));
  ScheduledFuture<?> future =  executorService.schedule(runnable, 
        delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS);

  executorService.schedule(new Runnable() {
      @Override
      public void run() {
        executorService.shutdown();
      }}, delay.toStandardDuration().getMillis(), TimeUnit.MILLISECONDS);

  return future;
}

Ran your code. 跑你的代码。 This is from the thread dump in jvisualvm (I named the thread "bla"): 这是来自jvisualvm中的线程转储(我命名为线程“bla”):

"bla" prio=5 tid=7fa2bc16a000 nid=0x10bd53000 runnable [10bd52000]
  java.lang.Thread.State: RUNNABLE
  at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:950)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
  at java.lang.Thread.run(Thread.java:680)

  Locked ownable synchronizers:
    - None

Now, this is from grepcode for that ThreadPoolExecutor.java:950 : 现在,这是来自于ThreadPoolExecutor.java:950 grepcode:

944         // It is possible (but unlikely) for a thread to have been
945         // added to workers, but not yet started, during transition to
946         // STOP, which could result in a rare missed interrupt,
947         // because Thread.interrupt is not guaranteed to have any effect
948         // on a non-yet-started Thread (see Thread#interrupt).
949         if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
950             t.interrupt();

Which leads me to conclude that the problem is in your testing code -- you are shutting down the executor too fast and the thread finds itself in a weird state. 这让我得出结论,问题出在您的测试代码中 - 您正在关闭执行程序太快,并且线程发现自己处于一种奇怪的状态。 This should not be an issue in production code. 这不应该是生产代码中的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM