Java線程池及線程池的如何使用


常見的四種線程池

newFixedThreadPool

固定大小的線程池,可以指定線程池的大小,該線程池corePoolSize和maximumPoolSize相等,阻塞隊列使用的是LinkedBlockingQueue,大小為整數最大值。

該線程池中的線程數量始終不變,當有新任務提交時,線程池中有空閑線程則會立即執行,如果沒有,則會暫存到阻塞隊列。對於固定大小的線程池,不存在線程數量的變化。同時使用無界的LinkedBlockingQueue來存放執行的任務。當任務提交十分頻繁的時候,LinkedBlockingQueue

迅速增大,存在着耗盡系統資源的問題。而且在線程池空閑時,即線程池中沒有可運行任務時,它也不會釋放工作線程,還會占用一定的系統資源,需要shutdown。

newSingleThreadExecutor

單個線程線程池,只有一個線程的線程池,阻塞隊列使用的是LinkedBlockingQueue,若有多余的任務提交到線程池中,則會被暫存到阻塞隊列,待空閑時再去執行。按照先入先出的順序執行任務。

newCachedThreadPool

 

緩存線程池,緩存的線程默認存活60秒。線程的核心池corePoolSize大小為0,核心池最大為Integer.MAX_VALUE,阻塞隊列使用的是SynchronousQueue。是一個直接提交的阻塞隊列,    他總會迫使線程池增加新的線程去執行新的任務。在沒有任務執行時,當線程的空閑時間超過keepAliveTime(60秒),則工作線程將會終止被回收,當提交新任務時,如果沒有空閑線程,則創建新線程執行任務,會導致一定的系統開銷。如果同時又大量任務被提交,而且任務執行的時間不是特別快,那么線程池便會新增出等量的線程池處理任務,這很可能會很快耗盡系統的資源。

newScheduledThreadPool

定時線程池,該線程池可用於周期性地去執行任務,通常用於周期性的同步數據。

scheduleAtFixedRate:是以固定的頻率去執行任務,周期是指每次執行任務成功執行之間的間隔。

schedultWithFixedDelay:是以固定的延時去執行任務,延時是指上一次執行成功之后和下一次開始執行的之前的時間。

 

使用實例  

newFixedThreadPool實例:

public class FixPoolDemo {

    private static Runnable getThread(final int i) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) {
        ExecutorService fixPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            fixPool.execute(getThread(i));
        }
        fixPool.shutdown();
    }
}

 

newCachedThreadPool實例:

public class CachePool {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                }catch (Exception e){

                }
                System.out.println(i);
            }
        };
    }

    public static  void main(String args[]){
        ExecutorService cachePool = Executors.newCachedThreadPool();
        for (int i=1;i<=10;i++){
            cachePool.execute(getThread(i));
        }
    }
}

這里沒用調用shutDown方法,這里可以發現過60秒之后,會自動釋放資源。

 

newSingleThreadExecutor

public class SingPoolDemo {
    private static Runnable getThread(final int i){
        return new Runnable() {
            @Override
            public void run() {
                try {

                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(i);
            }
        };
    }

    public static void main(String args[]) throws InterruptedException {
        ExecutorService singPool = Executors.newSingleThreadExecutor();
        for (int i=0;i<10;i++){
            singPool.execute(getThread(i));
        }
        singPool.shutdown();
    }

這里需要注意一點,newSingleThreadExecutor和newFixedThreadPool一樣,在線程池中沒有任務時可執行,也不會釋放系統資源的,所以需要shudown。

 

newScheduledThreadPool

public class ScheduledExecutorServiceDemo {
    public static void main(String args[]) {

        ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
        ses.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(4000);
                    System.out.println(Thread.currentThread().getId() + "執行了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 0, 2, TimeUnit.SECONDS);
    }
}

 

如何選擇線程池數量

線程池的大小決定着系統的性能,過大或者過小的線程池數量都無法發揮最優的系統性能。

當然線程池的大小也不需要做的太過於精確,只需要避免過大和過小的情況。一般來說,確定線程池的大小需要考慮CPU的數量,內存大小,任務是計算密集型還是IO密集型等因素

NCPU = CPU的數量

UCPU = 期望對CPU的使用率 0 ≤ UCPU ≤ 1

W/C = 等待時間與計算時間的比率

如果希望處理器達到理想的使用率,那么線程池的最優大小為:

線程池大小=NCPU *UCPU(1+W/C)

在Java中使用

int ncpus = Runtime.getRuntime().availableProcessors();

 

如何設置參數

  • 默認值
    • corePoolSize=1
    • queueCapacity=Integer.MAX_VALUE
    • maxPoolSize=Integer.MAX_VALUE
    • keepAliveTime=60s
    • allowCoreThreadTimeout=false
    • rejectedExecutionHandler=AbortPolicy()
      • 需要根據幾個值來決定
        • tasks :每秒的任務數,假設為500~1000
        • taskcost:每個任務花費時間,假設為0.1s
        • responsetime:系統允許容忍的最大響應時間,假設為1s
      • 做幾個計算
        • corePoolSize = 每秒需要多少個線程處理? 
          • threadcount = tasks/(1/taskcost) =tasks*taskcout =  (500~1000)*0.1 = 50~100 個線程。corePoolSize設置應該大於50
          • 根據8020原則,如果80%的每秒任務數小於800,那么corePoolSize設置為80即可
        • queueCapacity = (coreSizePool/taskcost)*responsetime
          • 計算可得 queueCapacity = 80/0.1*1 = 80。意思是隊列里的線程可以等待1s,超過了的需要新開線程來執行
          • 切記不能設置為Integer.MAX_VALUE,這樣隊列會很大,線程數只會保持在corePoolSize大小,當任務陡增時,不能新開線程來執行,響應時間會隨之陡增。
        • maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)
          • 計算可得 maxPoolSize = (1000-80)/10 = 92
          • (最大任務數-隊列容量)/每個線程每秒處理能力 = 最大線程數
        • rejectedExecutionHandler:根據具體情況來決定,任務不重要可丟棄,任務重要則要利用一些緩沖機制來處理
        • keepAliveTime和allowCoreThreadTimeout采用默認通常能滿足

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM