Java5,Java7實現的<線程池>綜述
系統啟動一個新線程的成本是比較高的,因為它涉及和操作系統交互.在這種情況下,線程池可以很好的提升性能.尤其是程序中需要創建大量生存周期很短暫的線程時,更應該考慮線程池.
運行機制:與數據庫連接池類似的是,線程池在系統啟動時就創建大量空閑的線程,程序將一個Runnable或者Callable對象傳給線程池,線程池就啟動一個線程來執行他們的run或者call方法.當方法結束后,該線程不會死亡.而是再次返回線程池中成為空閑狀態,等待下一個runnable/callable的run/call方法.
優點:使用線程池可以有效的控制系統中并發線程的數量,當系統中包含大量并發線程時,會導致系統性能急劇下降,導致JVM崩潰.而線程池的最大線程數參數可以控制系統中并發線程數不超過此數.
Java 5實現的線程池
從Java5開始,Java內建支持線程池.Java5新增了一個Executors工廠類來產生線程池.如下
- newCachedTheradPool():創建一個具有緩存功能的線程池,系統根據需要創建線程,這些線程將被緩存在線程池中.
- newFixedTheradPool(int threadNum):創建一個可重用,有固定線程數的線程池.
- newSingleThreadExecutor():等同上面方法傳入的參數為1.
- newScheduledThreadPool(int num):創建具有指定線程數的線程池,它可以在指定延遲后執行線程任務.num是指池中所保存的線程數,即使線程是空閑的也被保存在線程池內.
- newSingleThreadScheduledExecutor():創建只有一個線程的線程池,它可以在指定延遲后執行線程任務.
后2個方法返回都是一個ScheduledExecutorService對象,它是ExecutorService的子類,可以指定延遲后執行線程任務.
ExecutorService有三個方法:
- Future<?> submit(Runnable task):將一個Runnable對象提交給指定的線程池.Future<?>是代表Runnable的返回值,但run()方法沒有返回值,所以是最后返回null.所以可以根據Future的isDone(),isCancelled()方法來獲取Runnable對象的執行狀態.
- <T>Future<T> submit(Runnable task,T result):同上,result將顯式指定線程執行結束后的返回值,所以Future對象將在run方法執行結束后返回result.
- <T>Future<T> submit(Callable<T> task):將一個Callable對象提交給指定的線程池,其中Future表示Callable對象里call()方法的返回值.
- ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit):指定callable將在delay延遲后執行.
- ScheduledFuture<?> schedule(Runnable runnable,long delay,TimeUnit unit):指定runnable任務將在delay延遲后執行.
- ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable,long initialDelay,long period,TimeUnit unit):指定runnable任務在delay延遲后執行,而且以設定頻率重復執行,也就是說,在initialDelay后開始執行,一次在initialDelay+period,initialDelay+2*period...處重復執行,以此類推.
- ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable,long initialDelay,long delay,TimeUnit unit):創建并執行一個在給定初始延遲后首次啟用的定期操作,隨后在每一次執行終止和下一次執行開始之間都存在給定的延遲.如果任務在某一次執行的時候遇到異常,就取消后續執行,否則,只能通過程序來顯式取消或終止任務.
l例子:
package org.credo.thread.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class java5pool { public static void main(String[] args) { //創建一個具有固定線程數的線程池 ExecutorService pool=Executors.newFixedThreadPool(6); //向線程池中提交兩個線程 pool.submit(new MyThread()); pool.submit(new MyThread()); //關閉線程池 pool.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"的i值為:"+i); } } }
Java 7實現的線程池ForkJoinPool
ForkJoinPool
Java7提供它用來支持多核CPU的性能優勢.將一個任務拆分成多個"小任務"并行計算,再把多個"小任務"的結果合并成總的計算結果.ForkJoinPool是ExecutorService的實現類,因此是一種特殊的線程池.
它提供了如下兩個常用的構造器.
- ForkJoinPool(int num):創建一個保護num個并行線程的ForkJoinPool.
- ForkJoinPool():以Runtime.availableProcessors()方法的返回值作為"num參數"來創建ForkJoinPool.
ForkJoinTask代表一個可以并行,合并的任務.
ForkJoinTask是一個抽象類,它還有2個抽象子類:RecursiveAcion和RecursiveTask.其中RecursiveTask代表有返回值得任務.而RecursiveAction代表沒有返回值.
類圖如下:

package org.credo.thread.pool; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveAction; import java.util.concurrent.TimeUnit; public class java7pool { public static void main(String[] args) throws InterruptedException { ForkJoinPool pool=new ForkJoinPool(); //提交可分解的PrintTask任務 pool.submit(new PrintTask(0, 30)); //關閉線程池 pool.shutdown(); pool.awaitTermination(2, TimeUnit.SECONDS); } } //我CPU是I5.雙核四線程的CPU(虛擬四核實際雙核). //所以ForkJoinPool會啟動四個線程來執行這個打印任務. //而且不是連續打印,是因為程序將這個打印任務進行了分解.分解后并行執行任務. //RecursiveAction,沒有返回值 class PrintTask extends RecursiveAction{ private static final long serialVersionUID = -7849183204551061688L; //每個"小任務"最多打印的數次 threshold 極限/臨界強度 private static final int THRESHOLD=5; private int start; private int end; //打印從start到end的任務 public PrintTask(int start,int end){ this.start=start; this.end=end; } @Override protected void compute() { //當end與start之間的差小于threshold時,開始打印 if(end-start<THRESHOLD){ System.out.println("come here!"); for(int i=start;i<end;i++){ System.out.println(Thread.currentThread().getName()+"的i值:"+i+"==="+"start:"+start+" end:"+end); } }else{ System.out.println("here it is!"); //當end和start差大于threshold,即打印數超過threshold //將大任務分解成2個小任務 int middle=(start+end)/2; PrintTask left=new PrintTask(start, middle); PrintTask right=new PrintTask(middle, end); left.fork(); right.fork(); } } }
輸出:
here it is! here it is! here it is! here it is! come here! here it is! come here! ForkJoinPool-1-worker-2的i值:11===start:11 end:15 here it is! here it is! come here! ForkJoinPool-1-worker-2的i值:12===start:11 end:15 ForkJoinPool-1-worker-4的i值:18===start:18 end:22 ForkJoinPool-1-worker-2的i值:13===start:11 end:15 ForkJoinPool-1-worker-3的i值:3===start:3 end:7 come here! ForkJoinPool-1-worker-3的i值:4===start:3 end:7 ForkJoinPool-1-worker-3的i值:5===start:3 end:7 ForkJoinPool-1-worker-2的i值:14===start:11 end:15 ForkJoinPool-1-worker-4的i值:19===start:18 end:22 come here! ForkJoinPool-1-worker-3的i值:6===start:3 end:7 ForkJoinPool-1-worker-1的i值:26===start:26 end:30 come here! ForkJoinPool-1-worker-2的i值:7===start:7 end:11 ForkJoinPool-1-worker-4的i值:20===start:18 end:22 ForkJoinPool-1-worker-2的i值:8===start:7 end:11 ForkJoinPool-1-worker-3的i值:0===start:0 end:3 ForkJoinPool-1-worker-1的i值:27===start:26 end:30 ForkJoinPool-1-worker-3的i值:1===start:0 end:3 ForkJoinPool-1-worker-2的i值:9===start:7 end:11 ForkJoinPool-1-worker-4的i值:21===start:18 end:22 ForkJoinPool-1-worker-2的i值:10===start:7 end:11 ForkJoinPool-1-worker-3的i值:2===start:0 end:3 ForkJoinPool-1-worker-1的i值:28===start:26 end:30 come here! come here! ForkJoinPool-1-worker-2的i值:22===start:22 end:26 ForkJoinPool-1-worker-1的i值:29===start:26 end:30 ForkJoinPool-1-worker-2的i值:23===start:22 end:26 ForkJoinPool-1-worker-4的i值:15===start:15 end:18 ForkJoinPool-1-worker-4的i值:16===start:15 end:18 ForkJoinPool-1-worker-4的i值:17===start:15 end:18 ForkJoinPool-1-worker-2的i值:24===start:22 end:26 ForkJoinPool-1-worker-2的i值:25===start:22 end:26
上面程序中的PrintTask left=new PrintTask(start, middle);
PrintTask right=new PrintTask(middle, end);
left.fork();
right.fork();
對指定的打印任務進行了分解.分解后的任務分別調用fork方法開始并行執行.執行后就如上面的打印信息.
從結果來看,ForkJoinPool啟動了4個線程來執行.也可以看到程序打印的i的值不是連續的.因為程序把它進行了分解,分解后的任務會并行執行,不會按順序打印.