Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 (i.e. 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.
like image 732
Kes115 Avatar asked May 24 '12 12:05

Kes115


2 Answers

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!

like image 100
Eduardo Andrade Avatar answered Oct 21 '22 11:10

Eduardo Andrade


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

like image 36
John Vint Avatar answered Oct 21 '22 11:10

John Vint