程式扎記: [ Java 文章收集 ] ExecutorService usage tutorial

標籤

2015年1月20日 星期二

[ Java 文章收集 ] ExecutorService usage tutorial

Source From Here 
Preface 
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. (Youtube tutorial link

ExecutorService Example 
Here is a simple Java ExectorService example: 
  1. ExecutorService executorService = Executors.newFixedThreadPool(10);  
  2.   
  3. executorService.execute(new Runnable() {  
  4.     public void run() {  
  5.         System.out.println("Asynchronous task");  
  6.     }  
  7. });  
  8.   
  9. executorService.shutdown();  
First an ExectorService is created using the Executors.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 ExectorService 

Task Delegation 
Here is a diagram illustrating a thread delegating a task to an ExecutorService for asynchronous execution: 
 

Once the thread has delegated the task to the ExecutorService, the thread continues its own execution independent of the execution of that task. 

ExecutorService Implementations 
Since ExecutorService is an interface, you need to understand its implementations in order to make any use of it. The ExecutorService has the following implementation in the java.util.concurrent package: 
ThreadPoolExecutor 
Thread pools address two different problems: they usually provide improved performance when executing large numbers of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. Each ThreadPoolExecutor also maintains some basic statistics, such as the number of completed tasks...

ScheduledThreadPoolExecutor 
ThreadPoolExecutor that can additionally schedule commands to run after a given delay, or to execute periodically. This class is preferable to Timerwhen multiple worker threads are needed, or when the additional flexibility or capabilities of ThreadPoolExecutor (which this class extends) are required...

Creating an ExecutorService 
How you create an ExecutorService depends on the implementation you use. However, you can use the Executors factory class to create ExecutorServiceinstances too. Here are a few examples of creating an ExecutorService
  1. # Creates an Executor that uses a single worker thread operating off an unbounded queue.  
  2. ExecutorService executorService1 = Executors.newSingleThreadExecutor();  
  3.   
  4. # Creates a thread pool that reuses a fixed number of threads as 10 operating off a shared unbounded queue.  
  5. ExecutorService executorService2 = Executors.newFixedThreadPool(10);  
  6.   
  7. # Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically. (10 fixed threads here)  
  8. ExecutorService executorService3 = Executors.newScheduledThreadPool(10);  
ExecutorService Usage 
There are a few different ways to delegate tasks for execution to an ExecutorService
* execute(Runnable): Executes the given command at some time in the future.
* submit(Runnable): Submits a Runnable task for execution and returns a Future representing that task.
* submit(Callable): Submits a value-returning task for execution and returns a Future representing the pending results of the task.
* invokeAny(...): Executes the given tasks, returning the result of one that has completed successfully (i.e., without throwing an exception), if any do.
* invokeAll(...): Executes the given tasks, returning a list of Futures holding their status and results when all complete.

I will take a look at each of these methods in the following sections. 

execute(Runnable) 
The execute(Runnable) method takes a java.lang.Runnable object, and executes it asynchronously. Here is an example of executing a Runnable with anExecutorService
  1. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  2.   
  3. executorService.execute(new Runnable() {  
  4.     public void run() {  
  5.         System.out.println("Asynchronous task");  
  6.     }  
  7. });  
  8.   
  9. executorService.shutdown();  
There is no way of obtaining the result of the executed Runnable, if necessary. You will have to use a Callable for that (explained in the following sections). 

submit(Runnable) 
The submit(Runnable) method also takes a Runnable implementation, but returns a Future object. This Future object can be used to check if the Runnable as finished executing. Here is a ExecutorService submit() example: 
  1. Future future = executorService.submit(new Runnable() {  
  2.     public void run() {  
  3.         System.out.println("Asynchronous task");  
  4.     }  
  5. });  
  6.   
  7. future.get();  //returns null if the task has finished correctly.  
submit(Callable) 
The submit(Callable) method is similar to the submit(Runnable) method except for the type of parameter it takes. The Callable instance is very similar to aRunnable except that its call() method can return a result. The Runnable.run() method cannot return a result. 

The Callable's result can be obtained via the Future object returned by the submit(Callable) method. Here is an ExecutorService Callable example: 
  1. Future future = executorService.submit(new Callable(){  
  2.     public Object call() throws Exception {  
  3.         System.out.println("Asynchronous Callable");  
  4.         return "Callable Result";  
  5.     }  
  6. });  
  7.   
  8. System.out.println("future.get() = " + future.get());  
invokeAny() 
The invokeAny() method takes a collection of Callable objects, or subinterfaces of Callable. Invoking this method does not return a Future, but returns the result of one of the Callable objects. You have no guarantee about which of the Callable's results you get. Just one of the ones that finish. 

If one of the tasks complete (or throws an exception), the rest of the Callable's are cancelled. Here is a code example: 
  1. ExecutorService executorService = Executors.newFixedThreadPool(3);  
  2.   
  3. Set> callables = new HashSet>();  
  4.   
  5. callables.add(new Callable() {  
  6.     public String call() throws Exception {  
  7.         sleep 1000  
  8.         return "Task 1";  
  9.     }  
  10. });  
  11. callables.add(new Callable() {  
  12.     public String call() throws Exception {  
  13.         sleep 3000  
  14.         return "Task 2";  
  15.     }  
  16. });  
  17. callables.add(new Callable() {  
  18.     public String call() throws Exception {  
  19.         sleep 100  
  20.         return "Task 3";  
  21.     }  
  22. });  
  23.   
  24. printf("Blocking...\n")  
  25. String result = executorService.invokeAny(callables);  
  26. printf("Done!\n")  
  27.   
  28. System.out.println("result = " + result);  
  29.   
  30. // Wait for all tasks to be done!  
  31. executorService.shutdown();  
  32. printf("Exit main thread!")  
This code example will print out the object returned by one of the Callable's in the given collection. You can change sleep time in each Callable object to have different result. Below is the execution result: 
Blocking...
Done!
result = Task 3
Exit main thread!

invokeAll() 
The invokeAll() method invokes all of the Callable objects you pass to it in the collection passed as parameter. The invokeAll() returns a list of Future objects via which you can obtain the results of the executions of each Callable. Keep in mind that a task might finish due to an exception, so it may not have "succeeded". There is no way on a Future to tell the difference

Here is a code example: 
  1. import java.util.concurrent.Callable  
  2. import java.util.concurrent.ExecutorService  
  3. import java.util.concurrent.Executors  
  4. import java.util.concurrent.Future  
  5.   
  6. import flib.util.TimeStr  
  7.   
  8. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  9.   
  10. Set> callables = new HashSet>();  
  11.   
  12. long st = System.currentTimeMillis()  
  13. callables.add(new Callable() {  
  14.     public String call() throws Exception {  
  15.         sleep 3000  
  16.         return "Task 1";  
  17.     }  
  18. });  
  19. callables.add(new Callable() {  
  20.     public String call() throws Exception {  
  21.         sleep 2000  
  22.         return "Task 2";  
  23.     }  
  24. });  
  25. callables.add(new Callable() {  
  26.     public String call() throws Exception {  
  27.         sleep 1000  
  28.         return "Task 3";  
  29.     }  
  30. });  
  31.   
  32. printf("\t[Info] invokeAll...(%s)\n", TimeStr.ToStringFrom(st))  
  33. List> futures = executorService.invokeAll(callables);  
  34. printf("\t[Info] Iterate futures(%s):\n", , TimeStr.ToStringFrom(st))  
  35.   
  36. for(Future future : futures){  
  37.     System.out.println("\tfuture.get = " + future.get());  
  38. }  
  39. printf("\t[Info] Shutdown executor service...(%s)\n", TimeStr.ToStringFrom(st))  
  40. executorService.shutdown();  
  41. printf("\t[Info] Exit main thread...(%s)\n", TimeStr.ToStringFrom(st))  
From the execution result below, you can know that the returned futures will after all Callables are done! 
[Info] invokeAll...(21 ms)
[Info] Iterate futures(6 sec):
 <--- all="" b="" hang="" invokeall="" nbsp="" the="" untill="" will="">Callable
s are done!
future.get = Task 3
future.get = Task 2
future.get = Task 1
[Info] Shutdown executor service...(6 sec)
[Info] Exit main thread...(6 sec)

ExecutorService Shutdown 
When you are done using the ExecutorService you should shut it down, so the threads do not keep running

For instance, if your application is started via a main() method and your main thread exits your application, the application will keep running if you have an activeExexutorService in your application. The active threads inside this ExexutorService prevents 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 beforeshutdown() is called, are executed. Below is sample code for demonstration: 
  1. import java.util.concurrent.Callable  
  2. import java.util.concurrent.ExecutorService  
  3. import java.util.concurrent.Executors  
  4. import java.util.concurrent.Future  
  5.   
  6. import flib.util.TimeStr  
  7.   
  8. ExecutorService executorService = Executors.newSingleThreadExecutor();  
  9.   
  10. Set> callables = new HashSet>();  
  11.   
  12. long st = System.currentTimeMillis()  
  13. Callable longJob = new Callable() {  
  14.     public String call() throws Exception {  
  15.         sleep 10000  
  16.         printf("\t[Info] Long job done! (%s)\n", TimeStr.toStringFrom(st))  
  17.         return "Task 1";  
  18.     }  
  19. }  
  20.   
  21. printf("\t[Info] Submit long job...(%s)\n", TimeStr.toStringFrom(st))  
  22. Future future = executorService.submit(longJob)  
  23. printf("\t[Info] Wait 2 sec for job done...(%s)\n", TimeStr.toStringFrom(st))  
  24. sleep 2000  
  25. printf("\t[Info] Can't wait it anymore ><\"...(%s)\n", TimeStr.toStringFrom(st))  
  26.   
  27. printf("\t[Info] Plan to shutdown executor...(%s)\n", TimeStr.toStringFrom(st))  
  28. executorService.shutdown()  
  29. printf("\t[Info] Shutdown executor done...(%s)\n", TimeStr.toStringFrom(st))  
  30. printf("\t[Info] ...\n")  
  31. sleep 2000  
  32. printf("\t[Info] Can't terminate JVM...Try to cancel future object...(%s)\n", TimeStr.toStringFrom(st))  
  33. future.cancel(true)  
  34. printf("\t[Info] Cancel future done!...(%s)\n", TimeStr.toStringFrom(st))  
Execution result show that the shutdown or cancel from Future object can't stop already running Callable
[Info] Submit long job...(14 ms)
[Info] Wait 2 sec for job done...(48 ms)
[Info] Can't wait it anymore ><"...(2 sec)
[Info] Plan to shutdown executor...(2 sec)
[Info] Shutdown executor done...(2 sec)
[Info] ...
[Info] Can't terminate JVM...Try to cancel future...(4 sec)
[Info] Cancel future done!...(4 sec)
[Info] Long job done! (10 sec)

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 the executing tasks. Perhaps they stop, perhaps the execute until the end. It is a best effort attempt. 

Supplement 
ThreadPoolExecutor usage tutorial

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!