Q1. Executor Interfaces
Ans. The java.util.concurrent package defines three executor interfaces:
1. Executor: Simple interface that supports launching new tasks. It provides a single method - execute.
2. ExecutorService: Subinterface of Executor, which adds features that help manage the lifecycle, both of the individual tasks and of the executor itself.
3. ScheduledExecutorService: Subinterface of ExecutorService, supports future and/or periodic execution of tasks.
Executor Interface: However, the definition of execute is less specific. The low-level idiom creates a new thread and launches it immediately. Depending on the Executor implementation, execute may do the same thing, but is more likely to use an existing worker thread to run r, or to place r in a queue to wait for a worker thread to become available.
ScheduledExecutor Interface: Using either schedule() to run a task once or scheduleAtFixedRate() to repeat a task at a regular interval, you set up Runnable objects to be executed at some time in the future.Ans. The java.util.concurrent package defines three executor interfaces:
1. Executor: Simple interface that supports launching new tasks. It provides a single method - execute.
2. ExecutorService: Subinterface of Executor, which adds features that help manage the lifecycle, both of the individual tasks and of the executor itself.
3. ScheduledExecutorService: Subinterface of ExecutorService, supports future and/or periodic execution of tasks.
Executor Interface: However, the definition of execute is less specific. The low-level idiom creates a new thread and launches it immediately. Depending on the Executor implementation, execute may do the same thing, but is more likely to use an existing worker thread to run r, or to place r in a queue to wait for a worker thread to become available.
ExecutorService Interface: The ExecutorService interface supplements execute with a more versatile submit method. Like execute, submit accepts Runnable objects, but also accepts Callable objects, which allow the task to return a value. The submit method returns a Future object, which is used to retrieve the Callable return value and to manage the status of both Callable and Runnable tasks. ExecutorService also provides methods for submitting large collections of Callable objects. Finally, ExecutorService provides a number of methods for managing the shutdown of the executor. To support immediate shutdown, tasks should handle interrupts correctly.
ScheduledExecutorService Interface: The ScheduledExecutorService interface supplements the methods of its parent ExecutorService with schedule, which executes a Runnable or Callabletask after a specified delay. In addition, the interface defines scheduleAtFixedRate and scheduleWithFixedDelay, which executes specified tasks repeatedly, at defined intervals.
Q2. Difference b/w shutdown() and shutdownNow() of Executor interface.
Ans. The shutdown() and shutdownNow() methods are also part of the thread pool class. The shutdown() method is used to shut down the executor but allows all pending tasks to complete. The shutdownNow() method is used to try to cancel the tasks in the pool in addition to shutting down the thread pool. However, this works differently from a thread pool because of repeating tasks. Since certain tasks repeat, tasks could technically run forever during a graceful shutdown.
Q3. Why do you need Callable Interface
Ans. This introduced in Java SE5, is a generic with a type parameter representing the return value from the method call() and must be invoked using an ExecutorService submit() method.
Callable objects are used only by ExecutorService (not simple executors); the services operate on callable objects by invoking their call() method and keeping track of the results of those calls.
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<Future<String>>();
for (int i = 0; i < 10; i++)
results.add(exec.submit(new TaskWithResult(i)));
When you ask an executor service to run a callable object, the service returns a Future object that allows you to retrieve those results, monitor the status of the task, and cancel the task.
Callable and future objects have a one-to-one correspondence: every callable object that is sent to an executor service returns a matching future object. The get() method of the future object returns the results of its corresponding call() method. The get() method blocks until the call() method has returned (or until the optional timeout has expired). If the call() method throws an exception, the get() method throws an ExecutionException with an embedded cause, which is the exception thrown by the call() method.
The future object keeps track of the state of an embedded Callable object. The state is set to cancelled when the cancel() method is called. When the call() method of a callable task is called, the call() method checks the state: if the state is cancelled, the call() method immediately returns.
When the cancel() method is called, the corresponding callable object may be in one of three states. It may be waiting for execution, in which case its state is set to cancelled and the call() method is never executed. It may have completed execution, in which case the cancel( ) method has no effect. The object may be in the process of running. In that case, if the mayInterruptIfRunning flag is false, the cancel() method again has no effect. If the mayInterruptIfRunning flag is true, however, the thread running the callable object is interrupted. The callable object must still pay attention to this, periodically calling the Thread.interrupted() method to see if it should exit.
Q4. FutureTask Class/Future Interface
Ans. You can associate a Runnable or CALLABLE object with a future result using the FutureTask class. A FutureTask object can hold either an embedded runnable or callable object, depending on which constructor is used to instantiate the object.results.add(exec.submit(new TaskWithResult(i)));
When you ask an executor service to run a callable object, the service returns a Future object that allows you to retrieve those results, monitor the status of the task, and cancel the task.
Callable and future objects have a one-to-one correspondence: every callable object that is sent to an executor service returns a matching future object. The get() method of the future object returns the results of its corresponding call() method. The get() method blocks until the call() method has returned (or until the optional timeout has expired). If the call() method throws an exception, the get() method throws an ExecutionException with an embedded cause, which is the exception thrown by the call() method.
The future object keeps track of the state of an embedded Callable object. The state is set to cancelled when the cancel() method is called. When the call() method of a callable task is called, the call() method checks the state: if the state is cancelled, the call() method immediately returns.
When the cancel() method is called, the corresponding callable object may be in one of three states. It may be waiting for execution, in which case its state is set to cancelled and the call() method is never executed. It may have completed execution, in which case the cancel( ) method has no effect. The object may be in the process of running. In that case, if the mayInterruptIfRunning flag is false, the cancel() method again has no effect. If the mayInterruptIfRunning flag is true, however, the thread running the callable object is interrupted. The callable object must still pay attention to this, periodically calling the Thread.interrupted() method to see if it should exit.
Q4. FutureTask Class/Future Interface
public FutureTask(Callable<V> task);
public FutureTask(Runnable task, V result);
The get() method embedding a callable task returns whatever is returned by the call() method of that embedded object and get() method embedding a runnable object returns whatever object was used to construct the future task object itself.
Q5. What’s the difference b/w execute and submit method of Executor Interface.
Ans. Execute method accepts only Runnable and doesn’t return anything, whereas submit accepts both Runnable and callable and returns a Future object.
Q6. What is the use of Timer CLASS
Ans.
Ans.
cancel() method is used to stop the class from being executed. For a repeating task, calling the cancel() method prevents further execution of the class. For tasks that are executed only once, this has no affect if it is already executing. If task is currently running, has already run, or has been previously cancelled, it returns a boolean value of false. For repeating tasks, this method always returns a boolean value of true.
The cancel() method is provided by the Timer class to destroy the timer. All the tasks in the timer are simply cancelled, and no new tasks are allowed to be scheduled. The Timer object can no longer be used to schedule any more tasks. If a task is currently executing, it is allowed to finish; currently executing tasks are not interrupted.
scheduledExecutionTime() method is used to return the time at which the previous invocation of a repeating task occurred, if task is not running now. If the task is currently running, it is the time at which the task began.
There are a few important issues in the timer implementation, particularly for repeated tasks. First, only a single thread executes the tasks. While it is recommended that the tasks executed by the Timer class be short-lived, no check ensures that this is so. This means that if the Timer object is overwhelmed, a task may be executed at a time much later than the specified time. For repeated tasks, the schedule() method does not take this into account. The schedule time is allowed to drift, meaning that the next iteration of the task is based on the previous iteration. This is not very useful if the task is used to maintain a clock or other time-critical task.
There are a few important issues in the timer implementation, particularly for repeated tasks. First, only a single thread executes the tasks. While it is recommended that the tasks executed by the Timer class be short-lived, no check ensures that this is so. This means that if the Timer object is overwhelmed, a task may be executed at a time much later than the specified time. For repeated tasks, the schedule() method does not take this into account. The schedule time is allowed to drift, meaning that the next iteration of the task is based on the previous iteration. This is not very useful if the task is used to maintain a clock or other time-critical task.
This can be resolved by using scheduleAtFixedRate(). The next iteration of the task scheduled by the scheduleAtFixedRate() method is calculated from when the previous iteration was supposed to execute—not when the previous iteration actually executes, unlike schedule() method which takes into accoutnt when the previous execution actually occurred.
Since scheduleAtFixedRate() doesn't allow task drifting, using it in conjunction with scheduledExecutionTime() gives added advantage. It might happen that more than one iteration of the repeated task may be waiting to execute. As a result, a timer task may want to skip a particular execution if it knows that another execution is pending in the queue.
Q7. Why do we need scheduled executors. Why can’t we use Timer class.
Q7. Why do we need scheduled executors. Why can’t we use Timer class.
Ans.
1. Timer class starts only one thread. While it is more efficient than creating a thread per task, it is not an optimal solution. The optimal solution may be to use a number of threads between one thread for all tasks and one thread per task.
2. TimerTask class is not necessary. It is used to attach methods to the task itself, providing the ability to cancel the task and to determine the last scheduled time. This is not necessary - it is possible for the timer itself to maintain this information. It also restricts what can be considered a task. Classes used with the Timer class must extend the TimerTask class; this is not possible if the class already inherits from another class. ScheduledExecutor is much more flexible to allow any Runnable object to be used as the task to be executed.
3. Relying upon the run() method is too restrictive for tasks. run() method has no return variable, nor can it throw any type of exceptions other than run-time exceptions (and even if it could, the timer thread wouldn't know how to deal with it).
ScheduledThreadPoolExecutor class solves all three of these problems. It uses a thread pool and allows the developer to specify the size of the pool. It can work with both Runnable and Callable objects whereas Timer can work only with Runnable objects. It has 3 methods:
1. schedule() method is used to schedule a one-time task.
2. scheduleAtFixedRate() method is used to schedule a repeated task that is not allowed to drift. This is basically the same scheduling model as scheduleAtFixedRate() method of Timer class.
3. scheduleWithFixedDelay() method is used to schedule a repeated task where the period between the tasks remains constant; this is useful when the delay between iterations is to be fixed.
Ex: This model is better for animation since there is no reason to have animation cycles accumulate if the start times drift. If one cycle of the animation runs late, there is no advantage to running the next cycle earlier.
NOTE: schedule() can be used for Callable objects latter two methods don't have Callable support.
Q8. Why do and don’t we need a Thread Pool.
Ans.
Benefits of Thread Pool
1. Overhead of creating a thread is very high; by using a pool, we can gain some performance when the threads are reused. The degree to which this is true depends a lot on your program and its requirements. Creating a thread can take as much as a few hundred microseconds, which is a significant amount of time for some programs but not for all.
2. It allows for better program design. If your program has a lot of tasks to execute, its better to keep the thread management separate to thread pool, thus focusing only on program logic. With a thread pool, you simply create a task and send the task to the pool to be executed; this leads to much more elegant programs.
Disadvantages of Thread Pool
If your program is doing batch processing, or simply providing a single answer or report, i.e. intermediate results are not of concern, it doesn't really matter if you use as many threads as possible or a thread pool. That doesn't mean that you can expect to create thousands of threads with impunity, as threads take memory and this will have impact on system performance. Still, if your program design nicely separates into multiple threads and you're interested only in the end result of all those threads, a thread pool isn't necessary.
Thread pools are also not necessary when available CPU resources are adequate to handle all the work the program needs to do. In fact, in this case a thread pool may do more harm than good. Obviously, if your system has eight CPUs and you have only four threads in your thread pool, tasks wait for a thread even though four CPUs are idle. With a thread pool, you want to throttle the total number of threads so that they don't overwhelm your system, but you never want to have fewer runnable threads than CPUs.
Q9. How do thread pools work.
Ans. Thread pool is constructed with M core threads and N maximum threads. Usually at beginning, no threads are actually created though you can specify to pool to create the threads or core thread.
1. Timer class starts only one thread. While it is more efficient than creating a thread per task, it is not an optimal solution. The optimal solution may be to use a number of threads between one thread for all tasks and one thread per task.
2. TimerTask class is not necessary. It is used to attach methods to the task itself, providing the ability to cancel the task and to determine the last scheduled time. This is not necessary - it is possible for the timer itself to maintain this information. It also restricts what can be considered a task. Classes used with the Timer class must extend the TimerTask class; this is not possible if the class already inherits from another class. ScheduledExecutor is much more flexible to allow any Runnable object to be used as the task to be executed.
3. Relying upon the run() method is too restrictive for tasks. run() method has no return variable, nor can it throw any type of exceptions other than run-time exceptions (and even if it could, the timer thread wouldn't know how to deal with it).
ScheduledThreadPoolExecutor class solves all three of these problems. It uses a thread pool and allows the developer to specify the size of the pool. It can work with both Runnable and Callable objects whereas Timer can work only with Runnable objects. It has 3 methods:
1. schedule() method is used to schedule a one-time task.
2. scheduleAtFixedRate() method is used to schedule a repeated task that is not allowed to drift. This is basically the same scheduling model as scheduleAtFixedRate() method of Timer class.
3. scheduleWithFixedDelay() method is used to schedule a repeated task where the period between the tasks remains constant; this is useful when the delay between iterations is to be fixed.
Ex: This model is better for animation since there is no reason to have animation cycles accumulate if the start times drift. If one cycle of the animation runs late, there is no advantage to running the next cycle earlier.
NOTE: schedule() can be used for Callable objects latter two methods don't have Callable support.
Q8. Why do and don’t we need a Thread Pool.
Ans.
Benefits of Thread Pool
1. Overhead of creating a thread is very high; by using a pool, we can gain some performance when the threads are reused. The degree to which this is true depends a lot on your program and its requirements. Creating a thread can take as much as a few hundred microseconds, which is a significant amount of time for some programs but not for all.
2. It allows for better program design. If your program has a lot of tasks to execute, its better to keep the thread management separate to thread pool, thus focusing only on program logic. With a thread pool, you simply create a task and send the task to the pool to be executed; this leads to much more elegant programs.
Disadvantages of Thread Pool
If your program is doing batch processing, or simply providing a single answer or report, i.e. intermediate results are not of concern, it doesn't really matter if you use as many threads as possible or a thread pool. That doesn't mean that you can expect to create thousands of threads with impunity, as threads take memory and this will have impact on system performance. Still, if your program design nicely separates into multiple threads and you're interested only in the end result of all those threads, a thread pool isn't necessary.
Thread pools are also not necessary when available CPU resources are adequate to handle all the work the program needs to do. In fact, in this case a thread pool may do more harm than good. Obviously, if your system has eight CPUs and you have only four threads in your thread pool, tasks wait for a thread even though four CPUs are idle. With a thread pool, you want to throttle the total number of threads so that they don't overwhelm your system, but you never want to have fewer runnable threads than CPUs.
Q9. How do thread pools work.
Ans. Thread pool is constructed with M core threads and N maximum threads. Usually at beginning, no threads are actually created though you can specify to pool to create the threads or core thread.
A task enters the pool (via the thread pool's execute() method). Now one of five things happens:
1. If the pool has created less than M threads, it starts a new thread and runs the new task immediately. Even if some of the existing threads are idle, a new thread is created in the pool's attempt to reach M threads.
2. If the pool has between M and N threads and one of those threads is idle, the task is run by an idle thread.
3. If the pool has between M and N threads and all the threads are busy, the thread pool examines the existing work queue. If the task can be placed on the work queue without blocking, it's put on the queue and no new thread is started.
4. If the pool has between M and N threads, all threads are busy, and the task cannot be added to the queue without blocking, the pool starts a new thread and runs the task on that thread.
5. If the pool has N threads and all threads are busy, the pool attempts to place the new task on the queue. If the queue has reached its maximum size, this attempt fails and the task is rejected. Otherwise, the task is accepted and run when a thread becomes idle (and all previously queued tasks have run).
1. If the pool has created less than M threads, it starts a new thread and runs the new task immediately. Even if some of the existing threads are idle, a new thread is created in the pool's attempt to reach M threads.
2. If the pool has between M and N threads and one of those threads is idle, the task is run by an idle thread.
3. If the pool has between M and N threads and all the threads are busy, the thread pool examines the existing work queue. If the task can be placed on the work queue without blocking, it's put on the queue and no new thread is started.
4. If the pool has between M and N threads, all threads are busy, and the task cannot be added to the queue without blocking, the pool starts a new thread and runs the task on that thread.
5. If the pool has N threads and all threads are busy, the pool attempts to place the new task on the queue. If the queue has reached its maximum size, this attempt fails and the task is rejected. Otherwise, the task is accepted and run when a thread becomes idle (and all previously queued tasks have run).
Q10. Talk about CachedThreadPool and FixedThreadPool
Ans. CachedThreadPool: It will generally create as many threads as it needs during the execution of a program and then will stop creating new threads as it recycles the old ones.
FixedThreadPool: With the FixedThreadPool, you do expensive thread allocation once, up front, and you thus limit the number of threads. This saves time because you aren't constantly paying for thread creation overhead for every single task.
A SingleThreadExecutor is like a FixedThreadPool with a size of one thread. This is useful for anything you want to run in another thread continually (a long-lived task), such as a task that listens to incoming socket connections. Thus, a SingleThreadExecutor serializes the tasks that are submitted to it, and maintains its own (hidden) queue of pending tasks.
Executors.newCachedThreadPool();
Executors.newFixedThreadPool(5);
Return type of both is ExecutorService.
Q11. Does calling cancel method on a Thread object in a thread pool removes it from queue.
1. If the pool has more than M threads, the thread waits for a new task to be queued. If a new task is queued within the timeout period, the thread runs it. If not, the thread exits, reducing the total number of threads in the pool. The timeout period is a parameter used to construct the thread pool.
Ans. CachedThreadPool: It will generally create as many threads as it needs during the execution of a program and then will stop creating new threads as it recycles the old ones.
FixedThreadPool: With the FixedThreadPool, you do expensive thread allocation once, up front, and you thus limit the number of threads. This saves time because you aren't constantly paying for thread creation overhead for every single task.
A SingleThreadExecutor is like a FixedThreadPool with a size of one thread. This is useful for anything you want to run in another thread continually (a long-lived task), such as a task that listens to incoming socket connections. Thus, a SingleThreadExecutor serializes the tasks that are submitted to it, and maintains its own (hidden) queue of pending tasks.
Executors.newCachedThreadPool();
Executors.newFixedThreadPool(5);
Return type of both is ExecutorService.
Q11. Does calling cancel method on a Thread object in a thread pool removes it from queue.
Ans. When an object in a thread pool is cancelled, there is no immediate effect: the object still remains queued for execution. When the thread pool is about to execute the object, it checks the object's internal state, sees that it has been cancelled, and skips execution of the object. So, cancelling an object on a thread pool queue does not immediately make space in the thread pool's queue. Future calls to the execute() method may still be rejected, even though cancelled objects are on the thread pool's queue: the execute() method does not check the queue for cancelled objects.
One way to deal with this situation is to call the purge() method on the thread pool. The purge() method looks over the entire queue and removes any cancelled objects. One caveat applies: if a second thread attempts to add something to the pool (using the execute() method) at the same time the first thread is attempting to purge the queue, the attempt to purge the queue fails and the canceled objects remain in the queue. A better way to cancel objects with thread pools is to use the remove() method of the thread pool, which immediately removes the task from the thread pool queue. The remove() method can be used with standard runnable objects.
Q12. What happens when current task completes execution and there are no more tasks on queue to run.
Ans. If no tasks are on the queue, one of two things happens: One way to deal with this situation is to call the purge() method on the thread pool. The purge() method looks over the entire queue and removes any cancelled objects. One caveat applies: if a second thread attempts to add something to the pool (using the execute() method) at the same time the first thread is attempting to purge the queue, the attempt to purge the queue fails and the canceled objects remain in the queue. A better way to cancel objects with thread pools is to use the remove() method of the thread pool, which immediately removes the task from the thread pool queue. The remove() method can be used with standard runnable objects.
Q12. What happens when current task completes execution and there are no more tasks on queue to run.
1. If the pool has more than M threads, the thread waits for a new task to be queued. If a new task is queued within the timeout period, the thread runs it. If not, the thread exits, reducing the total number of threads in the pool. The timeout period is a parameter used to construct the thread pool.
Note: If the specified timeout is 0, the thread always exits, regardless of the requested minimum thread pool size.
2. If the pool has M or fewer threads, the thread blocks indefinitely waiting for a new task to be queued (unless the timeout was 0, in which case it exits). It runs the new task when available.
2. If the pool has M or fewer threads, the thread blocks indefinitely waiting for a new task to be queued (unless the timeout was 0, in which case it exits). It runs the new task when available.
Q13. What is the importance of pool size and queue used in Thread Pools. Ans. Choice of pool size and queue are important to getting the behavior you want. For a queue, you have three choices:
1. A SynchronousQueue, which effectively has a size of 0. In this case, whenever the pool tries to queue a task, it fails. The implication of this is tasks are either run immediately or are rejected immediately. Note that you can prevent rejection of a task if you specify an unlimited maximum number of threads, but this prevents the throttling benefit of using a thread pool in the first place.
2. An unbounded queue, such as a LinkedBlockingQueue with an unlimited capacity. In this case, adding a task to the queue always succeeds, which means that the thread pool never creates more than M threads and never rejects a task.
3. A bounded queue, such as a LinkedBlockingQueue with a fixed capacity or an ArrayBlockingQueue. Let's suppose that the queue has a bounds of P. As tasks are added to the pool, it creates threads until it reaches M threads. At that point, it starts queuing tasks until the number of waiting tasks reaches P. As more tasks are added, the pool starts adding threads until it reaches N threads. If we reach a state where N threads are active and P tasks are queued, additional tasks are rejected.
Q14. What happens to rejected tasks.
Ans. Depending on the type of queue you use in the thread pool, a task may be rejected by the execute() method. Tasks are rejected if the queue is full or if the shutdown() method has been called on the thread pool.
When a task is rejected, the thread pool calls the rejected execution handler associated with the thread pool. There is one rejected execution handler for the entire pool; it applies to all potential tasks. You can write your own rejected execution handler, or you can use one of four predefined handlers. Appropriate rejected execution handler then takes an appropriate action when a task is rejected.
2. An unbounded queue, such as a LinkedBlockingQueue with an unlimited capacity. In this case, adding a task to the queue always succeeds, which means that the thread pool never creates more than M threads and never rejects a task.
3. A bounded queue, such as a LinkedBlockingQueue with a fixed capacity or an ArrayBlockingQueue. Let's suppose that the queue has a bounds of P. As tasks are added to the pool, it creates threads until it reaches M threads. At that point, it starts queuing tasks until the number of waiting tasks reaches P. As more tasks are added, the pool starts adding threads until it reaches N threads. If we reach a state where N threads are active and P tasks are queued, additional tasks are rejected.
Q14. What happens to rejected tasks.
Ans. Depending on the type of queue you use in the thread pool, a task may be rejected by the execute() method. Tasks are rejected if the queue is full or if the shutdown() method has been called on the thread pool.
When a task is rejected, the thread pool calls the rejected execution handler associated with the thread pool. There is one rejected execution handler for the entire pool; it applies to all potential tasks. You can write your own rejected execution handler, or you can use one of four predefined handlers. Appropriate rejected execution handler then takes an appropriate action when a task is rejected.
No comments:
Post a Comment