1. Executor接口
public interface Executor {
void execute(Runnable command);
}
2. 線程池
Executors的工廠方法可以返回多種線程池:
i. newFixedThreadPool(int nThread),固定大小的線程池,一直維持nThread個線程;
ii. newCachedThreadPool(),根據負載,自動調整線程池線程個數;
iii. newSingleThreadExecutor(),單線程的Executor;
iv. newScheduledThreadPool(int nThread),固定大小的線程池,可以延遲和週期性的執行任務,類似於Timer()(java1.5之後就應該很少用它了);
3. 具有生命週期的executor
ExecutorService一般爲執行一組任務,它在沒有shutdown前會一直等待。
awaitTermination會阻塞當前調用線程,等待線程池中的線程執行完成,或者超時,注意之前必須先調用shutdown。
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// ... additional convenience methods for task submission
}
4. Callable和Future
Executor的基本執行單元是Runner,但Runner不能返回值,也不好取消等。
接口Callable可以返回計算的值:
public interface Callable<V> {
V call() throws Exception;
}
而Future則可以管理任務的生命週期:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException,
CancellationException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
CancellationException, TimeoutException;
}
ExecutorService.submit()方法會返回一個Future,可以用來獲取返回值,或者取消任務。
也可以實例化FutureTask。
5. CompletionService,BlockingQueue和Executor的組合
你可以提交(submit)一組Callable任務,且像隊列一樣在可用時獲取(poll或者taken)計算結果,結果都被包裝在Future裏。
引用書上的例子:
瀏覽器渲染HTML頁面,先從HTML中獲取圖片的地址,加入到任務隊列中,然後渲染文字,圖片到了後,就立即顯示。
package org.jamie.demo;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
class ImageData {
private final String data;
public String getData() {
return data;
}
public ImageData(String data) {
super();
this.data = data;
}
}
class ImageLoader {
private Random ran = new Random();
public ImageData load(String src) throws InterruptedException {
long loadTime = 0L;
do {loadTime = ran.nextInt(10000);} //load time between 1 secs and 10 secs.
while (1000 > loadTime);
Thread.sleep(loadTime);
return new ImageData("image data for " + src);
}
}
public class ExecutorServiceDemo {
private ImageLoader imageLoader = new ImageLoader();
public void renderHtmlPage() throws Throwable {
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
List<String> imagesSources = scanImageSources();
CompletionService<ImageData> cs = new ExecutorCompletionService<ImageData>(executor);
for (final String src : imagesSources) {
cs.submit(new Callable<ImageData> () {
@Override
public ImageData call() throws Exception {
return imageLoader.load(src);
}
});
}
randerText();
for (int i = 0; i < imagesSources.size(); ++i) {
try {
Future<ImageData> future = cs.poll(2, TimeUnit.SECONDS);
if (null != future) {
ImageData data = future.get();
randerImage(data);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw e.getCause();
}
}
executor.shutdown();
}
private void randerImage(ImageData data) {
System.out.println("image of HTML " + data.getData());
}
private void randerText() {
System.out.println("text content of HTML");
}
private List<String> scanImageSources() {
return Arrays.asList(
"/foo.png",
"/bar.png",
"/beauty.jpg",
"/food.png",
"/sex.png",
"/ml.png",
"/boring.png");
}
public static void main(String[] args) throws Throwable {
new ExecutorServiceDemo().renderHtmlPage();
}
}
輸出:
image of HTML image data for /beauty.jpg
image of HTML image data for /foo.png
image of HTML image data for /food.png
image of HTML image data for /bar.png