Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。他们的关系为:
并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后在提交给一个Executor执行,Executor.execute(Runnalbe) 。Executor在执行时使用内部的线程池完成操作。
一、创建线程池
Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor()
创建一个单线程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
01 |
Executor executor = Executors.newFixedThreadPool( 10 ); |
02 |
Runnable task = new Runnable() { |
05 |
System.out.println( "task over" ); |
08 |
executor.execute(task); |
10 |
executor = Executors.newScheduledThreadPool( 10 ); |
11 |
ScheduledExecutorService scheduler = (ScheduledExecutorService) executor; |
12 |
scheduler.scheduleAtFixedRate(task, 10 , 10 , TimeUnit.SECONDS); |
二、ExecutorService与生命周期
ExecutorService扩展了Executor并添加了一些生命周期管理的方法。一个Executor的生命周期有三种状态,运行 ,关闭 ,终止 。Executor创建时处于运行状态。当调用ExecutorService.shutdown()后,处于关闭状态,isShutdown()方法返回true。这时,不应该再想Executor中添加任务,所有已添加的任务执行完毕后,Executor处于终止状态,isTerminated()返回true。
如果Executor处于关闭状态,往Executor提交任务会抛出unchecked exception RejectedExecutionException。
1 |
ExecutorService executorService = (ExecutorService) executor; |
2 |
while (!executorService.isShutdown()) { |
4 |
executorService.execute(task); |
5 |
} catch (RejectedExecutionException ignored) { |
9 |
executorService.shutdown(); |
三、使用Callable,Future返回结果
Future<V>代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞。FutureTask<V>实现了Future<V>和Runable<V>。Callable代表一个有返回值得操作。
01 |
Callable<Integer> func = new Callable<Integer>(){ |
02 |
public Integer call() throws Exception { |
03 |
System.out.println( "inside callable" ); |
05 |
return new Integer( 8 ); |
08 |
FutureTask<Integer> futureTask = new FutureTask<Integer>(func); |
09 |
Thread newThread = new Thread(futureTask); |
13 |
System.out.println( "blocking here" ); |
14 |
Integer result = futureTask.get(); |
15 |
System.out.println(result); |
16 |
} catch (InterruptedException ignored) { |
17 |
} catch (ExecutionException ignored) { |
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
例子:并行计算数组的和。
01 |
package executorservice; |
03 |
import java.util.ArrayList; |
04 |
import java.util.List; |
05 |
import java.util.concurrent.Callable; |
06 |
import java.util.concurrent.ExecutionException; |
07 |
import java.util.concurrent.ExecutorService; |
08 |
import java.util.concurrent.Executors; |
09 |
import java.util.concurrent.Future; |
10 |
import java.util.concurrent.FutureTask; |
12 |
public class ConcurrentCalculator { |
14 |
private ExecutorService exec; |
15 |
private int cpuCoreNumber; |
16 |
private List<Future<Long>> tasks = new ArrayList<Future<Long>>(); |
19 |
class SumCalculator implements Callable<Long> { |
20 |
private int [] numbers; |
24 |
public SumCalculator( final int [] numbers, int start, int end) { |
25 |
this .numbers = numbers; |
30 |
public Long call() throws Exception { |
32 |
for ( int i = start; i < end; i++) { |
39 |
public ConcurrentCalculator() { |
40 |
cpuCoreNumber = Runtime.getRuntime().availableProcessors(); |
41 |
exec = Executors.newFixedThreadPool(cpuCoreNumber); |
44 |
public Long sum( final int [] numbers) { |
46 |
for ( int i = 0 ; i < cpuCoreNumber; i++) { |
47 |
int increment = numbers.length / cpuCoreNumber + 1 ; |
48 |
int start = increment * i; |
49 |
int end = increment * i + increment; |
50 |
if (end > numbers.length) |
52 |
SumCalculator subCalc = new SumCalculator(numbers, start, end); |
53 |
FutureTask<Long> task = new FutureTask<Long>(subCalc); |
55 |
if (!exec.isShutdown()) { |
67 |
public Long getResult() { |
69 |
for (Future<Long> task : tasks) { |
72 |
Long subSum = task.get(); |
74 |
} catch (InterruptedException e) { |
76 |
} catch (ExecutionException e) { |
1 |
int [] numbers = new int [] { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 10 , 11 }; |
2 |
ConcurrentCalculator calc = new ConcurrentCalculator(); |
3 |
Long sum = calc.sum(numbers); |
4 |
System.out.println(sum); |
四、CompletionService
在刚在的例子中,getResult()方法的实现过程中,迭代了FutureTask的数组,如果任务还没有完成则当前线程会阻塞,如果我们希望任意字任务完成后就把其结果加到result中,而不用依次等待每个任务完成,可以使CompletionService。生产者submit()执行的任务。使用者take()已完成的任务,并按照完成这些任务的顺序处理它们的结果 。也就是调用CompletionService的take方法是,会返回按完成顺序放回任务的结果,CompletionService内部维护了一个阻塞队列BlockingQueue,如果没有任务完成,take()方法也会阻塞。修改刚才的例子使用CompletionService:
01 |
public class ConcurrentCalculator2 { |
03 |
private ExecutorService exec; |
04 |
private CompletionService<Long> completionService; |
07 |
private int cpuCoreNumber; |
10 |
class SumCalculator implements Callable<Long> { |
14 |
public ConcurrentCalculator2() { |
15 |
cpuCoreNumber = Runtime.getRuntime().availableProcessors(); |
16 |
exec = Executors.newFixedThreadPool(cpuCoreNumber); |
17 |
completionService = new ExecutorCompletionService<Long>(exec); |
22 |
public Long sum( final int [] numbers) { |
24 |
for ( int i = 0 ; i < cpuCoreNumber; i++) { |
25 |
int increment = numbers.length / cpuCoreNumber + 1 ; |
26 |
int start = increment * i; |
27 |
int end = increment * i + increment; |
28 |
if (end > numbers.length) |
30 |
SumCalculator subCalc = new SumCalculator(numbers, start, end); |
31 |
if (!exec.isShutdown()) { |
32 |
completionService.submit(subCalc); |
46 |
public Long getResult() { |
48 |
for ( int i = 0 ; i < cpuCoreNumber; i++) { |
50 |
Long subSum = completionService.take().get(); |
52 |
} catch (InterruptedException e) { |
54 |
} catch (ExecutionException e) { |