Java5,Java7實現的<線程池>綜述

jopen 11年前發布 | 29K 次閱讀 Java Java開發

系統啟動一個新線程的成本是比較高的,因為它涉及和操作系統交互.在這種情況下,線程池可以很好的提升性能.尤其是程序中需要創建大量生存周期很短暫的線程時,更應該考慮線程池.

運行機制:與數據庫連接池類似的是,線程池在系統啟動時就創建大量空閑的線程,程序將一個Runnable或者Callable對象傳給線程池,線程池就啟動一個線程來執行他們的run或者call方法.當方法結束后,該線程不會死亡.而是再次返回線程池中成為空閑狀態,等待下一個runnable/callable的run/call方法.

優點:使用線程池可以有效的控制系統中并發線程的數量,當系統中包含大量并發線程時,會導致系統性能急劇下降,導致JVM崩潰.而線程池的最大線程數參數可以控制系統中并發線程數不超過此數.

Java 5實現的線程池

從Java5開始,Java內建支持線程池.Java5新增了一個Executors工廠類來產生線程池.如下

  1. newCachedTheradPool():創建一個具有緩存功能的線程池,系統根據需要創建線程,這些線程將被緩存在線程池中.
  2. newFixedTheradPool(int threadNum):創建一個可重用,有固定線程數的線程池.
  3. newSingleThreadExecutor():等同上面方法傳入的參數為1.
  4. newScheduledThreadPool(int num):創建具有指定線程數的線程池,它可以在指定延遲后執行線程任務.num是指池中所保存的線程數,即使線程是空閑的也被保存在線程池內.
  5. newSingleThreadScheduledExecutor():創建只有一個線程的線程池,它可以在指定延遲后執行線程任務.
前3個方法返回都是一個ExecutorService對象,該對象代表一個線程池.而且是立即執行.
后2個方法返回都是一個ScheduledExecutorService對象,它是ExecutorService的子類,可以指定延遲后執行線程任務.
ExecutorService有三個方法:
  1. Future<?> submit(Runnable task):將一個Runnable對象提交給指定的線程池.Future<?>是代表Runnable的返回值,但run()方法沒有返回值,所以是最后返回null.所以可以根據Future的isDone(),isCancelled()方法來獲取Runnable對象的執行狀態.
  2. <T>Future<T> submit(Runnable task,T result):同上,result將顯式指定線程執行結束后的返回值,所以Future對象將在run方法執行結束后返回result.
  3. <T>Future<T> submit(Callable<T> task):將一個Callable對象提交給指定的線程池,其中Future表示Callable對象里call()方法的返回值.
ScheduledExecutorService代表的是指定延遲后或周期性的執行線程任務的線程池.提供四個方法:
  1. ScheduledFuture<V> schedule(Callable<V> callable,long delay,TimeUnit unit):指定callable將在delay延遲后執行.
  2. ScheduledFuture<?> schedule(Runnable runnable,long delay,TimeUnit unit):指定runnable任務將在delay延遲后執行.
  3. ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable,long initialDelay,long period,TimeUnit unit):指定runnable任務在delay延遲后執行,而且以設定頻率重復執行,也就是說,在initialDelay后開始執行,一次在initialDelay+period,initialDelay+2*period...處重復執行,以此類推.
  4. 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的實現類,因此是一種特殊的線程池.
它提供了如下兩個常用的構造器.

  1. ForkJoinPool(int num):創建一個保護num個并行線程的ForkJoinPool.
  2. ForkJoinPool():以Runtime.availableProcessors()方法的返回值作為"num參數"來創建ForkJoinPool.
創建了ForkJoinPool實例之后,就可以調用ForkJoinPool的submit(ForkJoinTask task)或invoke(ForkJoinTask task)方法來執行指定任務了.
ForkJoinTask代表一個可以并行,合并的任務.
ForkJoinTask是一個抽象類,它還有2個抽象子類:RecursiveAcion和RecursiveTask.其中RecursiveTask代表有返回值得任務.而RecursiveAction代表沒有返回值.

類圖如下:
Java5,Java7實現的<線程池>綜述

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的值不是連續的.因為程序把它進行了分解,分解后的任務會并行執行,不會按順序打印.

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!