線程池基本使用

jdk1.5引入Executor線程池框架,經過它把任務的提交和執行進行解耦,只須要定義好任務,而後提交給線程池,而不用關心該任務是如何執行、被哪一個線程執行,以及何時執行。

初始化線程池(4種)

簡介:

Java線程池的工廠類:Executors類,數組

初始化4種類型的線程池:緩存

newCachedThreadPool()
說明:初始化一個能夠緩存線程的線程池,默認緩存60s,線程池的線程數可達到Integer.MAX_VALUE,即2147483647,內部使用SynchronousQueue做爲阻塞隊列;
特色:在沒有任務執行時,當線程的空閒時間超過keepAliveTime,會自動釋放線程資源;當提交新任務時,若是沒有空閒線程,則建立新線程執行任務,會致使必定的系統開銷;
所以,使用時要注意控制併發的任務數,防止因建立大量的線程致使而下降性能。併發

newFixedThreadPool()
說明:初始化一個指定線程數的線程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene做爲阻塞隊列
特色:即便當線程池沒有可執行任務時,也不會釋放線程。
newSingleThreadExecutor()
說明:初始化只有一個線程的線程池,內部使用LinkedBlockingQueue做爲阻塞隊列。
特色:保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。若是該線程異常結束,會從新建立一個新的線程繼續執行任務,惟一的線程能夠保證所提交任務的順序執行
newScheduledThreadPool()
特定:初始化的線程池能夠在指定的時間內週期性的執行所提交的任務,在實際的業務場景中可使用該線程池按期的同步數據。框架

總結:除了newScheduledThreadPool的內部實現特殊一點以外,其它線程池內部都是基於ThreadPoolExecutor類(Executor的子類)實現的。ide

newCachedThreadPool和newSingleThreadExecutor使用例子結果對比:

public class MyRunnable implements Runnable {
    private Integer index;

    public MyRunnable(Integer index) {
        this.index = index;
    }

    @Override
    public void run() {
        System.out.println(index);
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new MyRunnable(i));
        }
        executorService.shutdown();
    }
}

結果:性能

public class MyRunnable implements Runnable {
    private Integer index;

    public MyRunnable(Integer index) {
        this.index = index;
    }

    @Override
    public void run() {
        System.out.println(index);
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new MyRunnable(i));
        }
        executorService.shutdown();
    }
}

結果:優化

newScheduleThreadPool使用例子:

建立一個定長的線程池,並且支持定時的以及週期性的任務執行,支持定時及週期性任務執行。this

延遲3秒執行,延遲執行示例代碼以下:spa

public class MyThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("delay 3 seconds");
            }
        }, 3, TimeUnit.SECONDS);
        scheduledThreadPool.shutdown();
    }
}

表示延遲1秒後每3秒執行一次,按期執行示例代碼以下:線程

public class MyThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("delay 1 seconds, and excute every 3 seconds");
            }
        }, 1, 3, TimeUnit.SECONDS);
        try {
            Thread.sleep(10000);
            scheduledThreadPool.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

調用shutdown就會結束線程池退休循環

 

ThreadPoolExecutor內部具體實現:

ThreadPoolExecutor類構造器語法形式:

ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,timeUnit,workQueue,threadFactory,handle);   

方法參數:
   corePoolSize:核心線程數
   maxPoolSize:最大線程數
     keepAliveTime:線程存活時間(在corePore<*<maxPoolSize狀況下有用)
     timeUnit:存活時間的時間單位
     workQueue:阻塞隊列(用來保存等待被執行的任務)

注:關於workQueue參數的取值,JDK提供了4種阻塞隊列類型供選擇:
        ArrayBlockingQueue:基於數組結構的有界阻塞隊列,按FIFO排序任務;
        LinkedBlockingQuene:基於鏈表結構的阻塞隊列,按FIFO排序任務,吞吐量一般要高於  

            SynchronousQuene:一個不存儲元素的阻塞隊列,每一個插入操做必須等到另外一個線程調用移除操做,不然插入操做一直處於阻塞狀態,吞吐量一般要高於ArrayBlockingQuene;

        PriorityBlockingQuene:具備優先級的無界阻塞隊列

     threadFactory:線程工廠,主要用來建立線程;

     handler:表示當拒絕處理任務時的策略,有如下四種取值

 注: 當線程池的飽和策略,當阻塞隊列滿了,且沒有空閒的工做線程,若是繼續提交任務,必須採起一種策略處理該任務,線程池提供了4種策略:

    ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。

    ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,可是不拋出異常。

    ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,而後從新嘗試執行任務(重複此過程)

    ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務

    固然也能夠根據應用場景實現RejectedExecutionHandler接口,自定義飽和策略,如記錄日誌或持久化存儲不能處理的任務。

線程池的狀態(5種)

其中AtomicInteger變量ctl的功能很是強大:利用低29位表示線程池中線程數,經過高3位表示線程池的運行狀態:
一、RUNNING:-1 << COUNT_BITS,即高3位爲111,該狀態的線程池會接收新任務,並處理阻塞隊列中的任務;
二、SHUTDOWN: 0 << COUNT_BITS,即高3位爲000,該狀態的線程池不會接收新任務,但會處理阻塞隊列中的任務;
三、STOP : 1 << COUNT_BITS,即高3位爲001,該狀態的線程不會接收新任務,也不會處理阻塞隊列中的任務,並且會中斷正在運行的任務;
四、TIDYING : 2 << COUNT_BITS,即高3位爲010,該狀態表示線程池對線程進行整理優化;
五、TERMINATED: 3 << COUNT_BITS,即高3位爲011,該狀態表示線程池中止工做;


 向線程池提交任務(2種)

有兩種方式:

      Executor.execute(Runnable command);

      ExecutorService.submit(Callable<T> task);

execute()內部實現

1.首次經過workCountof()獲知當前線程池中的線程數,

  若是小於corePoolSize, 就經過addWorker()建立線程並執行該任務;

 不然,將該任務放入阻塞隊列;

2. 若是能成功將任務放入阻塞隊列中,  

若是當前線程池是非RUNNING狀態,則將該任務從阻塞隊列中移除,而後執行reject()處理該任務;

若是當前線程池處於RUNNING狀態,則須要再次檢查線程池(由於可能在上次檢查後,有線程資源被釋放),是否有空閒的線程;若是有則執行該任務;

三、若是不能將任務放入阻塞隊列中,說明阻塞隊列已滿;那麼將經過addWoker()嘗試建立一個新的線程去執行這個任務;若是addWoker()執行失敗,說明線程池中線程數達到maxPoolSize,則執行reject()處理任務;

 sumbit()內部實現

會將提交的Callable任務會被封裝成了一個FutureTask對象

FutureTask類實現了Runnable接口,這樣就能夠經過Executor.execute()提交FutureTask到線程池中等待被執行,最終執行的是FutureTask的run方法; 

比較:

 兩個方法均可以向線程池提交任務,execute()方法的返回類型是void,它定義在Executor接口中, 而submit()方法能夠返回持有計算結果的Future對象,它定義在ExecutorService接口中,它擴展了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。 


線程池的關閉(2種)

  ThreadPoolExecutor提供了兩個方法,用於線程池的關閉,分別是shutdown()和shutdownNow(),其中:

    shutdown():不會當即終止線程池,而是要等全部任務緩存隊列中的任務都執行完後才終止,但不再會接受新的任務

    shutdownNow():當即終止線程池,並嘗試打斷正在執行的任務,而且清空任務緩存隊列,返回還沒有執行的任務


 線程池容量的動態調整

  ThreadPoolExecutor提供了動態調整線程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

相關文章
相關標籤/搜索