Pages

April 01, 2016

Part 7: Java Thread Interview Questions & Answers (Thread Pool, Executor)



What is ThreadLocal variable in Java?

ThreadLocal is a special class for which each thread accessing the variable would have a separate instance of it. This was introduced on JDK 1.2 but it was later generified in JDK 1.4 to introduce type safety on ThreadLocal variable.

The ThreadLocal variable becomes eligible for Garbage collection after the thread finished or died, normally or due to any Exception (given those ThreadLocal variable doesn't have any other live references).

ThreadLocal is a generic class and can be applied to any type.

ThreadLocal variables in Java are generally private static fields in Classes and maintain its state inside Thread. e.g :

private static final ThreadLocal < SimpleDateFormat > dateformatter = new ThreadLocal < SimpleDateFormat > ();

How is the ThreadLocal variable initialized?

By overriding the initialValue() method:
private static final ThreadLocal < Integer > count = new ThreadLocal < > () {
  @Override
  public Integer initialValue() {
    return Integer.valueOf(0);
  }
};

What is the difference between Runnable and Callable in Java?

1. Runnable is there from JDK 1.0 while Callable was added on JDK 1.5.
2. Runnable interface has run() method to define task while Callable interface uses call() method for task definition.
3. run() method does not return any value, its return type is void while call method returns java.lang.Object type. The Callable interface is a generic parameterized interface and the Type of value is provided when an instance of Callable implementation is created.
4. One more difference is that the Callable interface can throw checked exception, while Runnable can't [run() does not throw any checked exception)

What is a thread pool?

We all know, creating a thread is expensive in terms of time and resources. If we create a thread at the time of request processing it will slow down our response time, also there is only a limited number of threads a process can create.

A Thread Pool gives us an option to avoid these issues. When an application starts up, a pool of threads is created and the threads are reused for request processing. This pool of thread is known as 'thread pool' and threads are known as 'worker thread'.

Why thread pool uses Blocking Queue?

A Blocking Queue is the special type of Queue that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.

Whenever we submit/ execute a new task using the ExecutorService, the ExecutorService will internally store the tasks in the queue. All the threads available in the thread pool will perform two steps: a). fetch the next task from the queue, b). execute it. 

Since all the threads available in the thread pool will attempt to take the task from the queue and execute it at the same time (i.e concurrently), a queue that is thread-safe is needed. That's the reason the thread pool uses a Blocking queue.

BlockingQueue works on the following rules:
  • If fewer than poolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If poolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.

What is the ideal thread pool size?

One Java thread corresponds to one OS thread. If we have a CPU with 5 cores, then at a time we can have only 5 threads running. No matter how many threads you create via thread pool, only 5 threads will be executed at a time (time split scheduling). That means having too many threads is not particularly a good idea when we have CPU-intensive operations. In this case, no. of thread pool size = no. of the core in the CPU.

We can get no. of Core's in the CPU from Runtime.getRuntime().availableProcessors(). e.g:


Now, let's take an example where the task has an IO intensive operation (e.g fetching data from the database or a task to call a REST API to get the data), then we can have the size of the pool which is greater than no. of the cores in the CPU.

Why? Because in this case, the task will have to wait till the response is received (maybe from REST API or from the DB), threads will remain in a waiting state until a response is received. So, even though we have 'x' no. of cores in CPU, there is no point in having 'x' threads because all the threads might be in a waiting state for the operation to finish and there are no threads to fetch and execute the next task.

In short:
If the task type is CPU intensive: Ideal pool size= CPU core count. If the CPU is running other applications your application might not get access to all the cores of the CPU.

If the task type is IO intensive Ideal pool size should be high. The pool size depends on the rate of the task submissions and the average task wait time. Too many threads will increase memory consumption too.

What are Executor, Executorservice, and ScheduledExecutorService?

  • These interfaces are present in java.util.concurrent package.
  • ExecutorService, and Executor are part of Java's Executor framework which provides thread pool facilities. 
  • Till Java 1.5, it was the application developer's responsibility to create and manage such thread pool, but from JDK 5 onward Executor framework provides a variety of built-in thread pools in Java e.g. fixed thread pool which contains a fixed number of threads and cached thread pool which can spawn new threads when needed.
  • The Executor interface is the parent, which is extended by ExecutorService.
  • The executor has the execute() method which accepts an object of the Runnable interface, while submit() method of ExecutorService can accept objects of both Runnable and Callable interfaces.
  • execute() method doesn't return any result, its return type is void, while but submit() method returns the result of computation via a Future object.
  • Apart from allowing a client to submit a task, ExecutorService also provides methods to control the thread pool e.g. terminate the thread pool by calling the shutDown() method.
  • Executors class provides factory methods to create different kinds of thread pools e.g. newSingleThreadExecutor() creates a thread pool of just one thread, newFixedThreadPool(int numOfThreads) creates a thread pool of fixed number of threads, and newCachedThreadPool() creates new threads when needed but reuse the existing threads if they are available.
  • ScheduledExecutorService is a sub-interface of ExecutorService, to execute commands periodically or after a given delay.

How to terminate a thread in Executor Framework in java?

When we are done using the ExecutorService we should shut it down, so the threads do not keep running. e.g: if our application is started via a main() method and the main thread exits our application, the application will keep running if we have an active ExexutorService in our application. The active threads inside this ExecutorService prevent the JVM from shutting down.

To terminate the threads inside the ExecutorService you call its shutdown() method. The ExecutorService will not shut down immediately, but it will no longer accept new tasks, and once all threads have finished current tasks, the ExecutorService shuts down. All tasks submitted to the ExecutorService before shutdown() is called, are executed.

If you want to shut down the ExecutorService immediately, you can call the shutdownNow() method. This will attempt to stop all executing tasks right away and skips all submitted but non-processed tasks. There are no guarantees given about executing tasks. Perhaps they stop, perhaps they execute until the end. It is a best-effort attempt.

What are the Lifecycle Methods of ExecutorService?
submit() extends base method Executor.execute(java.lang.Runnable) by creating and returning a Future that can be used to cancel execution and/or wait for completion.

Methods invokeAny and invokeAll execute a collection of tasks and then wait for at least one, or all, to complete, respectively. 

shutdown() and shutdownNow(): An ExecutorService can be shut down, which will cause it to reject new tasks. The shutdown() method will allow previously submitted tasks to execute before terminating, while the shutdownNow() method prevents waiting for tasks from starting and attempts to even stop currently executing tasks. Tasks submitted to an ExecutorService after it has been shut down are handled by the rejected execution handler, which might discard the  task or might cause execute to throw the unchecked RejectedExecutionException. 

awaitTermination(): Once all tasks have completed, the  ExecutorServic transitions to the terminated state. The awaitTermination(long timeout, TimeUnit unit) method blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted.

isShutdown() and isTerminated(): These methods return true if this executor has been shut down or if an executor is in terminated state, respectively.

What is the difference between Executor.submit() and Executor.execute() method in Java?

Both methods are used to submit a task to thread pools. execute(Runnable runnnableObj) is defined in Executor interface and executes given task in future, but more importantly, it does not return anything (because the return type is void).

On the other hand, submit() is an overloaded method, it can take either Runnable or Callable task and can return Future object which can hold the pending result of the computation. This method is defined on ExecutorService interface, which extends Executor interface, and every other thread pool class e.g. ThreadPoolExecutor or ScheduledThreadPoolExecutor gets these methods.

Difference between a Thread and an Executor in Java?

  • java.lang.Thread is a class in Java while java.util.concurrent.Executor is an interface.
  • The Executor allows your task is to be executed by a worker thread from the thread pool, while Thread itself executes your task.
  • Executor provides a execute() method which accepts a Runnable task, while Thread accepts the Runnable task on its constructor.
  • Thread can only execute one Runnable task but an Executor can execute any number of Runnable tasks.
  • In the case of a thread, it's the developer's responsibility to create and start the thread, but in the case of the Executor, the framework will create and start threads for you.

What are the types of thread pools available in Java?

FixedThreadPool: Create a thread pool of fixed size, if the pool is exhausted, tasks must wait (queued) till a thread becomes free. e.g
ExecutorService service=Executors.newFixedThreadPool(noOfThreads);

CachedThreadPool: Creates a thread pool of unlimited size, but if the threads get freed up, they are reused. Here we do not have the queue, which holds no. of tasks that we have submitted, instead, the queue is replaced with a Synchronous queue, that's why every time you submit a task, it will check if any of the threads are available (free) or not if no such thread is available it will create a thread, add it in the pool and ask the thread to execute the task. If any of the thread is idle for 60 seconds (has no task to execute), then that thread will be killed by the thread pool. e.g
ExecutorService service=Executors.newCachedThreadPool();

ScheduledThreadPool: This is specifically for the task, which you want to execute after a certain delay.
  • Service.schedule: You want to perform login checks, security checks after every 10 seconds, you can use Service.schedule to give it a delay of 10 seconds. In this case, the delay queue will be used.
  • Service.scheduleAtFixedRate: Use this when you want to perform recurring checks, say after every 10 seconds. It will store all the tasks you have submitted in the queue but do not execute, then sequentially, instead the submitted tasks will be executed according to the fixed delay (which is provided). If you have added 8 tasks each with a delay of 10 minutes10-second and then you add a new task with a delay of 10 seconds. The task with a 10-second delay will come in front.
  • Service.scheduleAtFixedDelay: If there is a scenario, where you want to run a task 'x' seconds after the previous instance has run use scheduleAtFixedDelay. This will complete the task, wait for a specified time and then execute the next task. e.g:

ScheduledExecutorService service=Executors.newScheduledThreadPool(coreCount);
service.scheduleAtFixedRate(new CpuIntensiveTask(), 10, 15, TimeUnit.SECONDS);

where: 10 is the time to delay the first execution and 15 is the period between successive executions

SingleThreadedExecutor: Creates only a single thread, tasks are executed sequentially from the queue (blocking). If the thread is killed because of some exception, the ThreadPool will recreate the thread.

What are constructor parameters and the life cycle methods of ExecutorService in Java?

When we instantiate our Executor Service, a few parameters are initialized. Depending on how we instantiated our Executor Service, you may manually specify these parameters or they may be provided for us by default. These parameters are:
  • corePool size is the minimum number of threads that should be kept in the pool. The number of threads may grow to reach the max pool size (if it is higher than the core pool size), but in general, it represents the number of threads you expect to have alive in the pool. When a task is submitted to the executor, it checks if the actual running number of threads is less than the core pool size. If it is, then it creates a new worker using the specified thread factory.
  • maxPool size is the maximum number of workers that can be in the pool. If the max pool size is greater than the core pool size, it means that the pool can grow in size, i.e. more workers can be added to the pool. Workers are added to the pool when a task is submitted but the work queue is full. Every time this happens, a new worker is added until the max pool size is reached. If the max pool size has already been reached and the work queue is full, then the next task will be rejected.
  • workQueue is used to queue up tasks for the available worker threads. The queue can be bounded or unbounded. For bounded queues, setting the queue size is an important exercise, as it affects how the worker pool grows and when you start running into RejectedExecutionExceptions. If you have a work-pool that you expect to grow; say from a core pool size of 20 workers to a max of 100 workers, then you may not want to set the queue size to a number that is too high, like 10,000 because it means that 10,000 tasks must be enqueued before each additional worker gets added to the pool. Unbounded queues and bounded queues with very high capacities are more suited to be used with fixed-size pools (i.e. pools where the core and max pools sizes are the same).
  • keepAliveTime: If the current number of worker threads exceeds the core pool size and a keepAliveTime is set, then worker threads are shut down when there is no more work to do until the number of worker threads is back to the core pool size; a thread will wait for work for the keepAlive time, and when that is exceeded and no work arrives, it will shut down.
  • threadFactory: The factory used to create new threads.
  • rejectedExecutionHandler: Callback to use when tasks submitted are rejected.

You can set allowCoreThreadTimeOut to true on your ThreadPoolExecutor instance, and if you do so, then not only do workers threads that exceed the core pool size get shut down on idle but core worker threads also get shut down on idle. By default, this is set to false. i.e the core pool threads are never killed unless allowCoreThreadTimeOut is set to true. By default, it's set to false.

ALSO READ: All About SynchronousQueue in Java


How does Executor work?

The executor picks up a thread from the threadpool to execute a task. If a thread is not available and new threads cannot be created, then the executor stores these tasks in a queue. A task can also be removed from the queue. If the queue is full, then the queue will start rejecting the tasks.

ALSO READ: All about BlockingQueue!

After the task is completed, the framework will not terminate the executing thread immediately. The executor keeps a minimum number of threads in the thread pool even if all of them are not executing some task. But it will terminate the extra threads (number of threads which are greater than the minimum number of threads) after the specified duration.

Whenever we create thread-pool from Executors.newCachedThreadPool or Executors.newFixedThreadPool or Executors.newSingleThreadExecutor() or Executors.newScheduledThreadPool() it internally creates a working queue.

  • For FixedThreadPool and SingleThreadExecutor, a LinkedBlockingQueue is created because threads are limited, thus unbounded queue is required to store all the tasks. Since the queue can never become full, new threads are never created.
  • For CachedThreadPool, a SynchronousQueue is created since the threads are unbounded, thus no need to store the tasks. FYI, a SynchronousQueue is a queue with a single slot.
  • For ScheduledThreadPool, a DelayedWorkQueue is created. It's a special queue that deals with schedules or time delays.

What is the role of Executors.unconfigurableExecutorService in Executor Framework?

Java Executors unconfigurableExecutorService() method returns an object that delegates all defined ExecutorService methods to the given executor, but not any other methods that might otherwise be accessible using casts. This provides a way to safely freeze configuration and disallow tuning of a given concrete implementation.

What is the role of ExecutorService in Java?

The java.util.concurrent.ExecutorService interface represents an asynchronous execution mechanism which is capable of executing tasks in the background. An ExecutorService is thus very similar to a thread pool. In fact, the implementation of ExecutorService present in the java.util.concurrent package is a thread pool implementation. e.g:

ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
    public void run() {
        System.out.println("Asynchronous task");
    }
});
executorService.shutdown();


First, an ExecutorService is created using the newFixedThreadPool() factory method. This creates a thread pool with 10 threads executing tasks.

Second, an anonymous implementation of the Runnable interface is passed to the execute() method. This causes the Runnable to be executed by one of the threads in the ExecutorService.


Output:
Running a thread with name :pool-1-thread-2, for task id:1
Running a thread with name :pool-1-thread-5, for task id:4
Running a thread with name :pool-1-thread-2, for task id:5
Running a thread with name :pool-1-thread-2, for task id:7
Running a thread with name :pool-1-thread-4, for task id:3
Running a thread with name :pool-1-thread-1, for task id:0
Running a thread with name :pool-1-thread-3, for task id:2
Running a thread with name :pool-1-thread-1, for task id:10
Running a thread with name :pool-1-thread-4, for task id:9
Running a thread with name :pool-1-thread-2, for task id:8
Running a thread with name :pool-1-thread-5, for task id:6
Running a thread with name :pool-1-thread-2, for task id:14
Running a thread with name :pool-1-thread-2, for task id:16
Running a thread with name :pool-1-thread-4, for task id:13
Running a thread with name :pool-1-thread-1, for task id:12
Running a thread with name :pool-1-thread-3, for task id:11
Running a thread with name :pool-1-thread-1, for task id:19
Running a thread with name :pool-1-thread-4, for task id:18
Running a thread with name :pool-1-thread-2, for task id:17
Running a thread with name :pool-1-thread-5, for task id:15

What is the role of FutureTask and Future in java?

FutureTask is a base concrete implementation of the Future interface and provides asynchronous processing. It contains the methods to start and cancel a task and also methods that can return the state of the FutureTask as whether it’s completed or canceled.

We need a callable object to create a future task and then we can use Java Thread Pool Executor to process these asynchronously.

A FutureTask can be used to wrap a Callable or Runnable object. Because FutureTask implements Runnable, a FutureTask can be submitted to an Executor for execution.

How to use Callable and Future in Java?

The run() method of Runnable interface does not return any value, so if you need to get a value back from the now-completed task, you must use a method outside the interface and wait for some kind of notification message that the task completed. e.g:

Runnable runnable = ...;
Thread t = new Thread(runnable);
t.start();
t.join();
String outValue = callMethodToGetTheValue();


With the ,Callable interface (which was introduced in J2SE 5.0) we can get the value by calling the call() method. The call() can return an Object or, more specifically, any type that is introduced in the generalized form.

public interface Callable< V  >   {
     V call() throws Exception;
}


We cannot pass a Callable into a Thread to execute, instead ExecutorService is used to execute the Callable object. The service accepts Callable objects to run by way of the submit() method:

< T  >   Future< T  >   submit(Callable< T  >   task)

As the method definition shows, submitting a Callable object to the ExecutorService returns a Future object. The get() method of Future will then block until the task is completed. This is the equivalent of the join() call. Actually, it is the equivalent of both the join() call and the get value call as get() returns the value calculated by the Callable instance.



Design your own custom Threadpool executor with minimal functionality?

GIT URL: Custom Threadpool executor



If you enjoy our content and want to support us, please consider donating via gpay, phonepay or paytm on +91 9920600280. Also, I’d appreciate it if you’d buy me a coffee☕ 


Keep learning and growing!
-K Himaanshu Shuklaa..

No comments:

Post a Comment