Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExecutorService SingleThreadExecutor

I have a list of objects, from which depending on user interaction some objects need to do work asynchronically. Something like this:

for(TheObject o : this.listOfObjects) {
   o.doWork();
}

The class TheObject implements an ExecutorService (SingleThread!), which is used to do the work. Every object of type TheObject instantiates an ExecutorService. I don't want to make lasagna code. I don't have enough Objects at the same time, to make an extra extraction layer with thread pooling needed.

I want to cite the Java Documentation about CachedThreadPools:

Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources.

First question: Is this also true for a SingleThreadExecutor? Does the thread get terminated? JavaDoc doesn't say anything about SingleThreadExecutor. It wouldn't even matter in this application, as I have an amount of objects I can count on one hand. Just curiosity.

Furthermore the doWork() method of TheObject needs to call the ExecutorService#.submit() method to do the work async. Is it possible (I bet it is) to call the doWork() method implicitly? Is this a viable way of designing an async method?

void doWork() {
   if(!isRunningAsync) {
      myExecutor.submit(doWork());
   } else {
      // Do Work...
   }
}
like image 837
El Mac Avatar asked Dec 25 '22 07:12

El Mac


2 Answers

First question: Is this also true for a SingleThreadExecutor? Does the thread get terminated?

Take a look at the source code of Executors, comparing the implementations of newCachedThreadPool and newSingleThreadExecutor:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

The primary difference (of interest here) is the 60L, TimeUnit.SECONDS and 0L, TimeUnit.MILLISECONDS.

Effectively (but not actually), these parameters are passed to ThreadPoolExecutor.setKeepAliveTime. Looking at the Javadoc of that method:

A time value of zero will cause excess threads to terminate immediately after executing tasks.

where "excess threads" actually refers to "threads in excess of the core pool size".

  • The cached thread pool is created with zero core threads, and an (effectively) unlimited number of non-core threads; as such, any of its threads can be terminated after the keep alive time.
  • The single thread executor is created with 1 core thread and zero non-core threads; as such, there are no threads which can be terminated after the keep alive time: its one core thread remains active until you shut down the entire ThreadPoolExecutor.

(Thanks to @GPI for pointing out that I was wrong in my interpretation before).

like image 50
Andy Turner Avatar answered Jan 10 '23 11:01

Andy Turner


First question:

Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources.

Is this also true for a SingleThreadExecutor?

SingleThreadExecutor works differently. It don't have time-out concept due to the values configured during creation.

Termination of SingleThread is possible. But it guarantees that always one Thread exists to handle tasks from task queue.

From newSingleThreadExecutor documentation:

public static ExecutorService newSingleThreadExecutor()

Creates an Executor that uses a single worker thread operating off an unbounded queue. (Note however that if this single thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks.)

Tasks are guaranteed to execute sequentially, and no more than one task will be active at any given time. Unlike the otherwise equivalent newFixedThreadPool(1) the returned executor is guaranteed not to be reconfigurable to use additional threads.

Second question:

Furthermore the doWork() method of TheObject needs to call the ExecutorService#.submit() method to do the work async

for(TheObject o : this.listOfObjects) {
   o.doWork();
}

can be changed to

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});

executorService.shutdown();

with Callable or Runnable interface and add your doWork() code in run() method or call() method. The task will be executed concurrently.

like image 42
Ravindra babu Avatar answered Jan 10 '23 10:01

Ravindra babu