Issues with Multithreading design:
Before java 1.5, Multithreading applications were created using Thread group, Thread Pool or Custom Thread Pool.
Here entire thread management was the responsibility of the programmer which are below:
Thread synchronization
Thread waiting
Thread joining
Thread locking
Thread notification
Handling deadlock
Thread behaviors are dependent on the environment where the application is deployed and running. So the same application
might behave in different way on different deployment environment based on the Processor speed, the RAM size, the bandwidth
etc. All have a direct impact on the multithreading application.
What is Executor Framework:
Executors framework (java.util.concurrent.Executor) is used for running the Runnable objects without creating new threads
every time and mostly re-using the already created threads. This provides multi-threading applications an easy abstraction layer.
The executor abstraction layer hides the critical parts of concurrent execution and the programmer only concentrates on the
business logic implementation.
In java executor framework all parallel works are considered as tasks instead of simple threads. So the application now deals
with instances of Runnable (basically collections of tasks or parallel works) and then it is passed to an Executor to process.
The ExecutorService interface extends the simplistic Executor interface. The 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.
Example:
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
ExecutorService executorService1 = Executors.newSingleThreadExecutor(); //Single thread to execute commands
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
ExecutorService executorService3 = Executors.newScheduledThreadPool(20);
ExecutorService executorService3 = Executors.newCachedThreadPool();
The newFixedThreadPool(int): returns a ThreadPoolExecutor instance with an initialized and unbounded queue and a
fixed number of threads. Here no extra thread is created during execution than the set value. So if there
is no free thread available the task has to wait and then execute when one thread is free.
The newCachedThreadPool(): returns a ThreadPoolExecutor instance initialized with an unbounded queue and unbounded
number of threads. Here existing threads are reused if available. But if no free thread is available, a new one
is created and added to the pool to complete the new task. Threads that have been idle for longer than a timeout period
will be removed automatically from the pool.
Different methods to delegate tasks for execution to an ExecutorService:
execute(Runnable)
submit(Runnable)
submit(Callable)
invokeAny(...)
invokeAll(...)
Q. Difference between "Executors.newSingleThreadExecutor().execute(command) and "new Thread(command).start()";
A. Once you have an Executor instance, you can submit multiple tasks to it, and have them executed one after another.
You can't do that simply with a raw Thread.
Executor framework creates tasks by using instances of Runnable or Callable. In case of Runnable, the run() method does not
return a value or throw any checked exception. But Callable is a more functional version in that area. It defines a call()
method that allows the return type as Object. This can be used in future processing and it also throws an exception if necessary.
The FutureTask class is another important component which is used to get future information about the processing. An
instance of this class can wrap either a Callable or a Runnable. You can get an instance of this as the return value of
submit() method of an ExecutorService. You can also manually wrap your task in a FutureTask before calling execute() method.
Apart from above Executors, here are the functional steps to implement the Java ThreadPoolExecutor:
A pool of multiple threads is created.
A queue is created holding all the tasks but these tasks are not yet assigned to threads from the pool.
Rejection handler is used to handle the situation when one or more tasks are not able to assign in the queue.
As per the default rejection policy, it will simply throw a RejectedExecutionException, a runtime exception, and the
application can catch it or discard it.
Creating Executors:
Executor is an interface having only "public abstract void execute(java.lang.Runnable)" method. Used to submit a new task.
ExecutorService is a sub-interface of Executor. It has other methods like "shutdown(), shutdownNow(), isTerminated(),
Future submit(Callable), Future submit(Runnable, Object), Future submit(Runnable)" etc.
----------------------------
Callable<String> myCommand2 = ...
ExecutorService executorService = ... // Build an executorService
executorService.submit(myCommand1);
//submit Accepts also a Callable
Future<String> resultFromMyCommand2 = executorService.submit(myCommand2);
//Will wait for myCommand1 and myCommand2 termination
executorService.shutdown();
Runnable myCommand3 = ...;
//Will throw a RejectedExecutionException because no new task can be submitted
executorService.submit(myCommand3);
----------------------------
ScheduledExecutorService is a sub-interface of ExecutorService and has "schedule(), scheduleAtFixedRate(),
scheduleWithFixedDelay()" methods. Used to execute commands periodically or after a given delay.
----------------------------
ScheduledExecutorService executor = ...;
Runnable command1 = ...;
Runnable command2 = ...;
Runnable command3 = ...;
//Will start command1 after 50 seconds
executor.schedule(command1, 50L, TimeUnit.SECONDS);
//Will start command 2 after 20 seconds, 25 seconds, 30 seconds ...
executor.scheduleAtFixedRate(command2, 20L, 5L, TimeUnit.SECONDS);
//Will start command 3 after 10 seconds and if command3 takes 2 seconds to be executed also after 17, 24, 31, 38 seconds...
executor.scheduleWithFixedDelay(command3, 10L, 5L, TimeUnit.SECONDS);
----------------------------
Executors is a Class and it has number of static factory methods to create an ExecutorService and
ScheduledExecutorService objects depending upon the requirement of the application.
----------------------------
ExecutorService ex3 = Executors.newSingleThreadExecutor();
Future future = ex3.submit(new Callable(){
public Object call() {
for(int i=20;i<=23;i++)
System.out.println("Asynchronous Callable: "+i);
return "My Result";
}
}
);
try {
System.out.println("Callable: "+future.get());
} catch (Exception e) {
e.printStackTrace();
}
ex3.shutdown();
System.out.println("All Executors are Shutdown...");
if(!ex3.isTerminated()) //Recheck if not shut down.
ex3.shutdownNow();
----------------------------
ThreadPool Executor:
private static final Executor executor = new ThreadPoolExecutor(6, 12, 5000L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(250));
The parameter values depend upon the application need. Here the core pool is having 6 threads which can run concurrently
and the maximum number is 12. The queue is capable of keeping 250 tasks. Here one point should be remembered that the pool
size should be kept on a higher side to accommodate all tasks. The idle time limit is kept as 5 ms.
Submit the task to the Executor: After creating the ExecutorService and proposed tasks, we need to submit the task to the
executor by using either submit() or execute() method. Now as per our configuration the tasks will be picked up from the queue
and run concurrently. For example if you have configured 5 concurrent executions, then 5 tasks will be picked up from the queue
and run in parallel. This process will continue till all the tasks are finished from the queue.
Execute the task: Next the actual execution of the tasks will be managed by the framework. The Executor is responsible for
managing the task’s execution, thread pool, synchronization and queue. If the pool has less than its configured number of
minimum threads, new threads will be created as per requirement to handle queued tasks until that limit is reached. If the
number is higher than the configured minimum, then the pool will not start any more threads. Instead, the task is queued
until a thread is freed up to process the request.
And Finally Shutdown the Executor: The termination is executed by invoking its shutdown() method or shutdownNow().
You can choose to terminate it gracefully, or abruptly.
Some Theories taken from : http://mrbool.com/working-with-java-executor-framework-in-multithreaded-application/27560
--------------------------------------------------------------------
Working Example:
----------------
package concurrency;
public class MyRunnable implements Runnable {
public void run(){
System.out.println("Run method");
}
}
----------------
//Sample ThreadPool Class to understand Executor Framework
package concurrency;
public class ThreadPool {
public static void main(String ar[]){
Thread worker[] = new Thread[3];
Runnable r = new MyRunnable();
System.out.println("Running ThreadPool Task..");
for(int i=0;i<worker.length;i++){
worker[i]=new Thread(r);
worker[i].start();
}
for(int i=0;i<worker.length;i++){
try {
worker[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
worker[i]=null;
}
}
}
----------------
//Above ThreadPool code is same as below Executor Framework
package concurrency;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExecutorTask {
public static void main(String[] args) {
int poolSize = 3;
int jobCount = 3;
Runnable r = new MyRunnable();
System.out.println("Running Executor Task..");
ExecutorService ex = Executors.newFixedThreadPool(poolSize);
for(int i=0;i<jobCount;i++){
ex.execute(r);
}
ex.shutdown();
System.out.println("Running Executor Task for Callable..");
List<Future> list = new ArrayList<Future>();
//ExecutorService executor = Executors.newFixedThreadPool(poolSize);
ExecutorService executor = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(15));
//CorePoolSize, MaxPoolSize, Alive Timeout (0 means lifetime), BlockingQueue
for(int i=0;i<jobCount;i++){
list.add(executor.submit(new MyCallable()));
}
try {
for(Future f: list)
System.out.println("Future Returned get: "+f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
}
}
----------------
package concurrency;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
public Object call(){
System.out.println("Call method..");
return "Server msg is Hi";
}
}
-----------------------------END---------------------------------------
Tuesday, February 14, 2017
Executor Framework in Java
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment