简体   繁体   中英

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). However, without executorService.shutdown(), nonDaemon threads never get garbage-collected after execution. 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)
  • Non-daemon threads get GC after execution.
  • 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. I made a unit test to prove that.

Test #1 (without calling future.get() method):

@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):

@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. When shutting down the TPE will have all its threads spin on the backing work queue until it is empty. 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. 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.

Also I have been told this is actually fixed in Java 7

Figured out a solution: It's shutdown() that changes the state of all pending task threads from 'waiting' to 'running'. Rather than calling shutdown() immediately, I now use the executorService to schedule the call to its own shutdown(). This ensures that pending tasks remain in the waiting state for as long as possible - thereby saving CPU resource.

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"):

"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 :

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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