??如果程序中創(chuàng)建了大量的生命期很短的線程,應該使用線程池(thread pool)。一個線程池中包含許多準備運行的空線程。將Runnable對象交給線程池,就會有一個線程調用run方法。當run方法退出時,線程不會死亡,而是在池中準備為下一個請求提供服務。
??另一個使用線程池的理由是減少并發(fā)線程的數(shù)目。創(chuàng)建大量線程會大大降低性能甚至使虛擬機崩潰。如果有一個會創(chuàng)建許多線程的算法,應該使用一個線程數(shù)“固定的”線程池以限制并發(fā)線程的總數(shù)。
??執(zhí)行器(Executor)類有許多靜態(tài)工廠方法用來構建線程池。
newCachedThreadPool | 必要時創(chuàng)建新線程;空閑線程會被保留60秒
newFixedThreadPool | 該池包含固定數(shù)量的線程;空閑線程會一直被保留。
newSingleThreadExecutor | 只有一個線程的“池”,該線程順序執(zhí)行每一個提交的任務
newScheduledThreadPool | 用于預定執(zhí)行而構建的固定線程池,替代java.util.Timer
newSingleThreadScheduledExecutor 用于預定執(zhí)行而構建的單線程“池”
1.線程池
- newCachedThreadPool方法構建了一個線程池,對于每個任務,如果有空線程可用,立即讓它執(zhí)行任務,如果沒有可用的空線程,則創(chuàng)建一個新線程。
- newFixedThreadPool方法構建一個具有固定大小的線程池。如果提交的任務數(shù)多于空閑的線程數(shù),那么把得不到服務的任務放置到隊列中。
- newSingleThreadExecutor是一個退化了的大小為1的線程池:由一個線程執(zhí)行提交的任務,一個接著一個。
??上面3個方法返回實現(xiàn)了ExecutorService接口的ThreadPoolExecutor類的對象。
??
??可以使用下面的方法將一個Runnable對象或Callable對象提交給ExecutorService:
//提交指定的任務去執(zhí)行
Future<?> submit(Runnable task)
Future<T> submit(Runnable task, T result)
Future<T> submit(Callable<T> task)
??該線程池會在方便的時候盡早執(zhí)行提交的任務。調用submit時,會得到一個Future對象,可用來查詢該任務的狀態(tài)。
??第一個submit方法返回一個Future<?>??梢允褂眠@樣一個對象來調用isDone、cancel或isCancelled。但是,get方法在完成的時候只是簡單地返回null。
??第二版本的Submit也提交一個Runnable,并且Future的get方法在完成的時候返回指定的result對象。
??第三個版本的Submit提交一個Callable,并且返回的Future對象將在計算結構準備好的時候得到它。
??當用完一個線程池的時候,調用shutdown。該方法啟動該池的關閉序列。被關閉的執(zhí)行器不再接受新的任務。當所有任務都完成以后,線程池中的線程死亡。另一種方法是調用shutdownNow。該池取消尚未開始的所有任務并試圖中斷正在運行的線程。
??總結在使用線程池時應該做的事:
1.調用Executors類中靜態(tài)的方法newCachedThreadPool或newFixedThreadPool。
2.調用submit提交Runnable或Callable對象。
3.如果想要取消一個任務,或如果提交Callable對象,那就要保存好返回的Future對象。
4.當不再提交任何任務時,調用shutdown。
??繼續(xù)以一個計算匹配的文件數(shù)目程序為例:
class MatchCounter implements Callable<Integer>
{
private File directory;
private String keyword;
private ExecutorService pool;
private int count;
public MatchCounter(File directory, String keyword, ExecutorService pool) {
this.directory = directory;
this.keyword = keyword;
this.pool = pool;
}
public Integer call() {
count = 0;
try {
File [] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for ( File file : files) {
if ( file.isDirectory()) {
MatchCounter counter = new MatchCounter(file, keyword, pool);
Future<Integer> result = pool.submit(counter);
results.add(result);
}
else {
if( search(file)) count++;
}
}
for (Future<Integer> result : results) {
try{
count += result.get();
}
catch (ExecutionException e) {
e.printStackTrace();
}
}
}
catch (InterruptedException e) {}
return count;
}
}
public boolean search(File file) {...}
public class FutureTest {
public static void main(String[] args) {
MatchCounter counter = new MatchCounter(new File(directory), keyword, pool);
Future<Integer> result = pool.submit(counter);
...
pool.shutdown();
}
}
2.預定執(zhí)行
??ScheduledExecutorService接口具有為預定執(zhí)行(Scheduled Execution)或重復執(zhí)行任務而設計的方法。它是一種允許使用線程池機制的java.util.Timer的泛化。Executors類的newScheduledThreadPool和newSingleThreadScheduledExecutor方法將返回實現(xiàn)了ScheduledExecutorService接口的對象。
??可以預定Runnable或Callable在初始的延遲之后只運行一次。也可以預定一個Runnable對象周期性地運行。詳見API。