Executors創建線程池的方法:
- public static ExecutorService newFixedThreadPool(int nThreads)
創建固定數目線程的線程池。 - public static ExecutorService newCachedThreadPool()
創建一個可緩存的線程池,調用execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。 - public static ExecutorService newSingleThreadExecutor()
創建一個單線程化的Executor。 - public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
創建一個支持定時及週期性的任務執行的線程池,多數情況下可用來替代Timer類。
Executor executor = Executors.newFixedThreadPool(10);
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("task over");
}
};
executor.execute(task);
executor = Executors.newScheduledThreadPool(10);
ScheduledExecutorService scheduler = (ScheduledExecutorService) executor;
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。
ExecutorService executorService = (ExecutorService) executor;
while (!executorService.isShutdown()) {
try {
executorService.execute(task);
} catch (RejectedExecutionException ignored) {
}
}
executorService.shutdown();
使用Callable,Future返回結果
Future代表一個異步執行的操作,通過get()方法可以獲得操作的結果,如果異步操作還沒有完成,則,get()會使當前線程阻塞。FutureTask實現了Future和Runable。Callable代表一個有返回值得操作。
Callable<Integer> func = new Callable<Integer>(){
public Integer call() throws Exception {
System.out.println("inside callable");
Thread.sleep(1000);
return new Integer(8);
}
};
FutureTask<Integer> futureTask = new FutureTask<Integer>(func);
Thread newThread = new Thread(futureTask);
newThread.start();
try {
System.out.println("blocking here");
Integer result = futureTask.get();
System.out.println(result);
} catch (InterruptedException ignored) {
} catch (ExecutionException ignored) {
}
ExecutoreService提供了submit()方法,傳遞一個Callable,或Runnable,返回Future。如果Executor後臺線程池還沒有完成Callable的計算,這調用返回Future對象的get()方法,會阻塞直到計算完成。
CompletionService
在剛在的例子中,getResult()方法的實現過程中,迭代了FutureTask的數組,如果任務還沒有完成則當前線程會阻塞,如果我們希望任意字任務完成後就把其結果加到result中,而不用依次等待每個任務完成,可以使CompletionService。生產者submit()執行的任務。使用者take()已完成的任務,並按照完成這些任務的順序處理它們的結果 。也就是調用CompletionService的take方法是,會返回按完成順序放回任務的結果,CompletionService內部維護了一個阻塞隊列BlockingQueue,如果沒有任務完成,take()方法也會阻塞。修改剛纔的例子使用CompletionService:
public class ConcurrentCalculator2 {
private ExecutorService exec;
private CompletionService<Long> completionService;
private int cpuCoreNumber;
// 內部類
class SumCalculator implements Callable<Long> {
......
}
public ConcurrentCalculator2() {
cpuCoreNumber = Runtime.getRuntime().availableProcessors();
exec = Executors.newFixedThreadPool(cpuCoreNumber);
completionService = new ExecutorCompletionService<Long>(exec);
}
public Long sum(final int[] numbers) {
// 根據CPU核心個數拆分任務,創建FutureTask並提交到Executor
for (int i = 0; i < cpuCoreNumber; i++) {
int increment = numbers.length / cpuCoreNumber + 1;
int start = increment * i;
int end = increment * i + increment;
if (end > numbers.length)
end = numbers.length;
SumCalculator subCalc = new SumCalculator(numbers, start, end);
if (!exec.isShutdown()) {
completionService.submit(subCalc);
}
}
return getResult();
}
/**
* 迭代每個只任務,獲得部分和,相加返回
*
* @return
*/
public Long getResult() {
Long result = 0l;
for (int i = 0; i < cpuCoreNumber; i++) {
try {
Long subSum = completionService.take().get();
result += subSum;
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
return result;
}
public void close() {
exec.shutdown();
}
}