1) Difference between Callable and Runnable
The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception.
2) ExecutorService Intro
"Executors are Java 5 name for the concept of thread pools. The 'Executor' naming is due to the fact that there is no guarantee that underlying implementation is actually a pool; an executor may be single-threaded or even synchronous".(From Spring Offical Doc)
Example:
package edu.xmu.thread; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ExecutorTest { public static void main(String[] args) { List<CalculationCallable> subThreadList = initSubThreadList(); List<Future<Integer>> futures = new ArrayList<Future<Integer>>(); ExecutorService pool = Executors.newFixedThreadPool(5); System.out.println("Start submit callable. Timestamp: " + System.currentTimeMillis()); for (CalculationCallable calThread : subThreadList) { futures.add(pool.submit(calThread)); } System.out .println("Finished submit callable. Start getFutures. Timestamp: " + System.currentTimeMillis()); int sum = 0; for (Future<Integer> future : futures) { try { sum += future.get(); } catch (Exception e) { e.printStackTrace(); } } System.out.println("Finished getFutures. sum: " + sum + ", Timestamp: " + System.currentTimeMillis()); pool.shutdown(); } private static List<CalculationCallable> initSubThreadList() { CalculationCallable thread1 = new CalculationCallable(Arrays.asList( 200, 200, 200, 200)); CalculationCallable thread2 = new CalculationCallable(Arrays.asList( 300, 300, 300, 300)); CalculationCallable thread3 = new CalculationCallable(Arrays.asList( 300, 300, 300, 300)); CalculationCallable thread4 = new CalculationCallable(Arrays.asList( 300, 300, 300, 300)); CalculationCallable thread5 = new CalculationCallable(Arrays.asList( 300, 300, 300, 300)); List<CalculationCallable> subThreadList = new ArrayList<CalculationCallable>(); subThreadList.add(thread1); subThreadList.add(thread2); subThreadList.add(thread3); subThreadList.add(thread4); subThreadList.add(thread5); return subThreadList; } } class CalculationCallable implements Callable<Integer> { private List<Integer> numList; public CalculationCallable(List<Integer> numList) { super(); this.numList = numList; } @Override public Integer call() throws Exception { int sum = 0; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } for (int i : numList) { sum += i; } return sum; } }
Output:
Start submit callable. Timestamp: 1400053633686 Finished submit callable. Start getFutures. Timestamp: 1400053633687 Finished getFutures. sum: 5600, Timestamp: 1400053635687 // We can see that submit(Callable) will not cause execution of Callable. // It just like a process of "register". // And future.get() will start the execution process instead. // And future.get() will start all the callables in the ExecutiveService(Pool)
Attention:
1> There is a disadvantage: If the first task takes a long time to compute and all the other tasks end before the first, the current thread cannot compute sum before the first task ends.
2> Java has the solution for this, CompletionService.
Extensions:
public static void main(String[] args) throws InterruptedException { List<CalculationCallable> subThreadList = initSubThreadList(); ExecutorService pool = Executors.newFixedThreadPool(5); System.out.println("Start submit callable. Timestamp: " + System.currentTimeMillis()); List<Future<Integer>> futures = pool.invokeAll(subThreadList); System.out .println("Finished submit callable. Start getFutures. Timestamp: " + System.currentTimeMillis()); int sum = 0; for (Future<Integer> future : futures) { try { sum += future.get(); } catch (Exception e) { e.printStackTrace(); } } System.out.println("Finished getFutures. sum: " + sum + ", Timestamp: " + System.currentTimeMillis()); pool.shutdown(); }
Output:
Start submit callable. Timestamp: 1400661124891 Finished submit callable. Start getFutures. Timestamp: 1400661126892 Finished getFutures. sum: 5600, Timestamp: 1400661126892 // We will notice that invokeAll(Collection<?>..) will start the execution of all threads // Instead of future.get(), we now bring the execution time forward.
The source code snippet for invokeAll(Collection<? extends Callable<T>>)
for (Future<T> f : futures) { if (!f.isDone()) { try { f.get(); } catch (CancellationException ignore) { } catch (ExecutionException ignore) { } } }
3) CompletionService Intro
Example:
public static void main(String[] args) { List<CalculationCallable> subThreadList = initSubThreadList(); ExecutorService threadPool = Executors.newFixedThreadPool(5); CompletionService<Integer> pool = new ExecutorCompletionService<Integer>( threadPool); System.out.println("Start submit callable. Timestamp: " + System.currentTimeMillis()); for (CalculationCallable calThread : subThreadList) { pool.submit(calThread); } System.out .println("Finished submit callable. Start getFutures. Timestamp: " + System.currentTimeMillis()); int sum = 0; for (int i = 0; i < 5; i++) { try { sum += pool.take().get(); } catch (Exception e) { e.printStackTrace(); } } System.out.println("Finished getFutures. sum: " + sum + ", Timestamp: " + System.currentTimeMillis()); threadPool.shutdown(); }
Output:
Start submit callable. Timestamp: 1400054835960 Finished submit callable. Start getFutures. Timestamp: 1400054835961 Finished getFutures. sum: 5600, Timestamp: 1400054837961 // The CompletionService is based on ExecutorService to work. // With that, you have the result in the order they are completed and you don't have to keep a collection of Future.
Reference Links:
1) http://baptiste-wicht.com/posts/2010/09/java-concurrency-part-7-executors-and-thread-pools.html
2) http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/scheduling.html