Java線程池ThreadPoolExecutor學習筆記java
1:核心線程:簡單來說就是線程池中可否容許同時併發運行的線程的數量緩存
2:線程池大小:線程池中最多可以容納的線程的數量。併發
3:隊列:對提交過來的任務的處理模式。異步
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize 核心線程數,指保留的線程池大小(不超過maximumPoolSize值時,線程池中最多有corePoolSize 個線程工做)。
maximumPoolSize 指的是線程池的最大大小(線程池中最大有corePoolSize 個線程可運行)。
keepAliveTime 指的是空閒線程結束的超時時間(當一個線程不工做時,過keepAliveTime 長時間將中止該線程)。
unit 是一個枚舉,表示 keepAliveTime 的單位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7個可選值)。
workQueue 表示存聽任務的隊列(存放須要被線程池執行的線程隊列)。
handler 拒絕策略(當添加任務數超過maximumPoolSize+workQueue數量時報錯).ide
運行時的分配機制學習
1)當池子大小小於corePoolSize就新建線程,並處理請求測試
2)當池子大小等於corePoolSize,把請求放入workQueue中,池子裏的空閒線程就去從workQueue中取任務並處理this
3)當workQueue放不下新入的任務時,新建線程入池,並處理請求,若是池子大小撐到了maximumPoolSize就用RejectedExecutionHandler來作拒絕處理atom
4)另外,當池子的線程數大於corePoolSize的時候,多餘的線程會等待keepAliveTime長的時間,若是無請求可處理就自行銷燬.net
private ThreadPoolExecutor poolExecutor; poolExecutor = new ThreadPoolExecutor(3, 10, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6)); for (int i = 0; i < 30; i++) { final int finalI = i; Runnable runnable = new Runnable() { @Override public void run() { Log.e("curentThread ","value: " + Thread.currentThread().getName() + "-----" + finalI); SystemClock.sleep(3000); } }; poolExecutor.execute(runnable); }
運行時會報錯,由於有30個任務須要運行,而緩存隊列只能緩存6個,剩下的任務須要去建立新線程,但是線程最多建立10因此就會越界報錯
若是new LinkedBlockingDeque<Runnable>()則表示隊列爲無限大,上面運行結果爲
全部的任務都存在隊列中,每次只有三個線程運行,因此顯示thread-1,2,3
poolExecutor = new ThreadPoolExecutor(3, 30, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(6));
若是,將maximumPoolSize改成30,則會出現20多個線程,由於隊列中只存在6個任務,剩下的20多個任務須要去申請新的線程,這個能夠本身運行查看效果
固定數量的線程池newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
這個線程池的核心線程與最大線程爲一個值,不等待,超出核心線程必定時間後的線程就被回收掉了。最多同時運行nThreads數量的線程。
單線程池
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
核心線程爲1的固定線程池
動態線程池(無界線程池)
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
核心線程池爲0,線程池的最大是無限,等待時間爲60秒,隊列爲直接提交。用的是SynchronousQueue隊列,沒有排隊等待直接添加線程。newCachedThreadPool比較適合沒有固定大小而且比較快速就能完成的小任務,不必維持一個Pool,這比直接new Thread來處理的好處是能在60秒內重用已建立的線程。機制就是查看已經建立的線程若是有空閒的(60秒之內超過就回收了)能夠直接使用,不用從新建立。
線程池雖然隊列能夠換成無窮多個任務,可是當任務過多時就會致使內存溢出,因此當任務很是多時,要使用有界隊列自定義線程池防止oom。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class CustomThreadPoolExecutor { private ThreadPoolExecutor pool = null; public void init() { pool = new ThreadPoolExecutor( 3, 10, 30, TimeUnit.MINUTES, new ArrayBlockingQueue<Runnable>(5), new CustomThreadFactory(), new CustomRejectedExecutionHandler()); } public void destory() { if(pool != null) { pool.shutdownNow(); } } public ExecutorService getCustomThreadPoolExecutor() { return this.pool; } private class CustomThreadFactory implements ThreadFactory { //原子操做 具體volatile private AtomicInteger count = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); String threadName = CustomThreadPoolExecutor.class.getSimpleName() + count.addAndGet(1); System.out.println(threadName); t.setName(threadName); return t; } } private class CustomRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { try { // 核心改造點,由blockingqueue的offer會拋棄任務,改爲put阻塞方法,這種不會丟失任務 executor.getQueue().put(r); } catch (InterruptedException e) { e.printStackTrace(); } } } // 測試構造的線程池 public static void main(String[] args) { CustomThreadPoolExecutor exec = new CustomThreadPoolExecutor(); // 1.初始化 exec.init(); ExecutorService pool = exec.getCustomThreadPoolExecutor(); for(int i=1; i<100; i++) { System.out.println("提交第" + i + "個任務!"); pool.execute(new Runnable() { @Override public void run() { try { System.out.println(">>>task is running====="); TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }
LinkedBlockingQueue添加方法put、offer、add介紹http://blog.csdn.net/z69183787/article/details/46986823
AtomicInteger介紹http://haininghacker-foxmail-com.iteye.com/blog/1401346
總結:
一、用ThreadPoolExecutor自定義線程池,看線程是的用途,若是任務量不大,能夠用無界隊列,若是任務量很是大,要用有界隊列,防止OOM
二、若是任務量很大,還要求每一個任務都處理成功,要對提交的任務進行阻塞提交,重寫拒絕機制,改成阻塞提交。保證不拋棄一個任務
三、最大線程數通常設爲2N+1最好,N是CPU核數
四、核心線程數,看應用,若是是任務,一天跑一次,設置爲0,合適,由於跑完就停掉了,若是是經常使用線程池,看任務量,是保留一個核心仍是幾個核心線程數
五、若是要獲取任務執行結果,用CompletionService,可是注意,獲取任務的結果的要從新開一個線程獲取,若是在主線程獲取,就要等任務都提交後才獲取,就會阻塞大量任務結果,隊列過大OOM,因此最好異步開個線程獲取結果