更多精彩請關注公衆號xhJaver,京東java工程師和你一塊兒成長
咱們知道,在計算機中建立一個線程和銷燬一個線程都是十分耗費資源的操做,有一種思想叫作,池化思想,就是說咱們建立個池子,把耗費資源的操做都提早作好,後面你們一塊兒用建立好的東西,最後統一銷燬。省去了用一次建立一次,銷燬一次,這種耗費資源的操做。java
線程池就是這種思想,他的基本工做流程以下圖所示數組
那麼他的核心線程,任務隊列,這些又是什麼呢?怎麼設置呢?安全
這些就要從代碼入手了,咱們先來看下線程池構造方法的代碼ide
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
其實ThreadPoolExecutor有四種構造方法,不過底層都是用這個7個參數的構造方法,因此咱們弄懂這一個就行了,如下是其餘構造方法的底層實現測試
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
其中默認的拒絕策略是this
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
這些構造方法中的atom
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
就是那七個參數的構造方法spa
有點懵?不要緊,接下來咱們一個個的解析這七個參數的意思線程
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
1. 第一個參數 corePoolSize 表明這個線程池的核心線程數日誌
2. 第二個參數 maximumPoolSize 表明這個線程池的最大線程數 (核心線程數 +非核心線程數)
3. 第三個參數 keepAliveTime 表明這個線程池的非核心線程的空閒時的存活時間
4. 第四個參數 unit 表明這個線程池的非核心線程的空閒存活時間的單位
5. 第五個參數 workQueue 表明這個線程池的任務阻塞隊列,jdk中有幾種常見的阻塞隊列
建議:建議使用有界隊列,要是無界隊列的話,任務太多的話可能會致使OOM
6. 第六個參數 threadFactory(能夠自定義) 表明這個線程池的建立線程的工廠,有兩種
7. 第七個參數 handler(能夠自定義) 表明這個線程池的拒絕處理任務的飽和策略,jdk默認提供了四種
基礎概念也都看了,下面來看個使用線程池處理任務的小例子
首先,咱們先建立個任務類
public class Task implements Runnable { private String taskName; public Task(String taskName) { this.taskName = taskName; } @Override public void run() { try { //模擬每一個任務的耗時 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String name = Thread.currentThread().getName(); System.out.println("這裏是xhJaver,線程池系列 當前線程名字是 " + name+" 處理了 "+ taskName+" 任務"); } }
咱們再來看測試類
public class Demo1 { public static void main(String[] args) { //阻塞隊列,設置阻塞任務最多爲10個 BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10); //線程工廠 ThreadFactory threadFactory = Executors.defaultThreadFactory(); //拒絕策略 當線程池的最大工做線程跑滿以及阻塞隊列滿了的話,會由拒絕策略處理剩下的任務 ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy(); //建立線程池 核心線程數爲5 最大線程數爲10 非核心線程空閒存活時間爲60s ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy ); for (int i=0;i<10;i++){ //建立10個任務,若是要是建立>20個任務,則20之外的任務會交由拒絕策略處理 Task task = new Task("task" + i); //讓咱們自定義的線程池去跑這些任務 threadPoolExecutor.execute(task); } //記得要關閉線程池 threadPoolExecutor.shutdown(); } }
輸出結果是
這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task0 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-2 處理了 task1 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-3 處理了 task2 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-4 處理了 task3 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-5 處理了 task4 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task5 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-2 處理了 task6 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-5 處理了 task9 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-4 處理了 task8 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-3 處理了 task7 任務
文中一直說線程工廠線程工廠,這線程工場究竟是幹嗎的呢? 固然是建立線程的工廠啦,建立線程,線程固然得有個名字咯,就像剛纔的小例子輸出的同樣,線程的名字是pool-1-thread-3等等,我如今不想叫這個名字了,那就叫thread-xhJaver吧,這是自定義的名字,那怎麼自定義呢?
首先,要實現ThreadFactory接口中的Thread newThread(Runnable r)方法, 傳入一個任務,返回一個自定義線程,以下面的代碼同樣
public class DIYThreadFactory implements ThreadFactory { private AtomicInteger atomicInteger; public DIYThreadFactory( AtomicInteger atomicInteger){ this.atomicInteger = atomicInteger; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("xhJaver-thread-"+atomicInteger.getAndIncrement()); return thread; } }
而後在使用時傳入這個自定義的線程工廠
public static void main(String[] args) { //阻塞隊列,設置阻塞任務最多爲10個 BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10); //建立線程安全的計數器 AtomicInteger atomicInteger = new AtomicInteger(); //自定義線程工廠 ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger); //拒絕策略 當線程池的最大工做線程跑滿以及阻塞隊列滿了的話,會由拒絕策略處理剩下的任務 ThreadPoolExecutor.AbortPolicy abortPolicy = new ThreadPoolExecutor.AbortPolicy(); //建立線程池 核心線程數爲5 最大線程數爲10 非核心線程空閒存活時間爲60s ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, blockingQueue, threadFactory, abortPolicy ); for (int i=0;i<10;i++){ //建立10個任務,若是要是建立>20個任務,則20之外的任務會交由拒絕策略處理 Task task = new Task("task" + i); //讓咱們自定義的線程池去跑這些任務 threadPoolExecutor.execute(task); } //記得要關閉線程池 threadPoolExecutor.shutdown(); }
輸出結果是
這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-0 處理了 task0 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-1 處理了 task1 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-4 處理了 task4 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-3 處理了 task3 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-2 處理了 task2 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-0 處理了 task5 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-1 處理了 task6 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-2 處理了 task9 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-3 處理了 task8 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-4 處理了 task7 任務
我也學會了自定義線程工廠了,可自定義名字到底有用呢,固然是排查問題啊!把線程名字定義爲和本身業務有關的名字,到時候報錯的時候就方便排查了。
線程工廠能夠自定義,那拒絕策略能夠自定義嗎?固然能夠啦 方法以下,首先也要實現一個RejectedExecutionHandler接口,重寫rejectedExecution 這個方法
public class DIYRejectedHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { //記錄日誌等操做 System.out.println("這是xhJaver沒法處理的任務 "+r.toString()+" 當前線程名字是 "+Thread.currentThread().getName()); } }
而後在使用時傳入這個自定義的拒絕策略
public static void main(String[] args) { //阻塞隊列,設置阻塞任務最多爲10個 BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10); //建立線程安全的計數器 AtomicInteger atomicInteger = new AtomicInteger(); //自定義線程工廠 ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger); //自定義拒絕策略 當線程池的最大工做線程跑滿以及阻塞隊列滿了的話,會由拒絕策略處理剩下的任務 DIYRejectedHandler diyRejectedHandler = new DIYRejectedHandler(); //建立線程池 核心線程數爲5 最大線程數爲10 非核心線程空閒存活時間爲60s ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, blockingQueue, threadFactory, diyRejectedHandler ); for (int i=0;i<30;i++){ //建立10個任務,若是要是建立>20個任務,則20之外的任務會交由拒絕策略處理 Task task = new Task("task" + i); //讓咱們自定義的線程池去跑這些任務 threadPoolExecutor.execute(task); } //記得要關閉線程池 threadPoolExecutor.shutdown(); }
輸出結果是
這是xhJaver沒法處理的任務 Task{taskName='task20'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task21'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task22'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task23'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task24'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task25'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task26'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task27'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task28'} 當前線程名字是 main 這是xhJaver沒法處理的任務 Task{taskName='task29'} 當前線程名字是 main 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-5 處理了 task15 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-4 處理了 task4 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-3 處理了 task3 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-2 處理了 task2 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-1 處理了 task1 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-0 處理了 task0 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-9 處理了 task19 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-8 處理了 task18 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-7 處理了 task17 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-6 處理了 task16 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-4 處理了 task6 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-5 處理了 task5 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-3 處理了 task7 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-2 處理了 task8 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-1 處理了 task9 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-0 處理了 task10 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-9 處理了 task11 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-8 處理了 task12 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-7 處理了 task13 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-6 處理了 task14 任務
由於阻塞隊列的知識太多了,後續咱們會單獨開篇來說這個阻塞隊列,先介紹幾個經常使用的
1.ArrayBlockingQueue 基於數組的有界隊列
2.LinkedBlockingQueue 基於鏈表的無界隊列
3.SynchronousQueue
它內部只有一個元素,插入時若是發現內部有元素未被取走則阻塞,取元素時若隊列沒有元素則被阻 塞,直到有元素插入進來。
搭配線程池使用以下 ,先建立任務類
public class Task implements Runnable { private String taskName; public Task(String taskName) { this.taskName = taskName; } @Override public String toString() { return "Task{" + "taskName='" + taskName + ''' + '}'; } @Override public void run() { try { //模擬每一個任務的耗時 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String name = Thread.currentThread().getName(); System.out.println("這裏是xhJaver,線程池系列 當前線程名字是 " + name+" 處理了 "+ taskName+" 任務"); } }
再使用阻塞隊列
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i=0;i<10;i++){ //建立十個任務 Task task = new Task("task" + i); //去跑任務 executorService.execute(task); } //記得要關閉線程池 executorService.shutdown(); }
其中newCachedThreadPool底層就使用的是SynchronousQueue
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
輸出結果是
這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task0 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-2 處理了 task1 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-5 處理了 task4 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-4 處理了 task3 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-3 處理了 task2 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-6 處理了 task5 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-7 處理了 task6 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-10 處理了 task9 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-9 處理了 task8 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-8 處理了 task7 任務
因而可知,線程池分別建立了十個線程來處理這十個任務,爲何呢? 這是由於,我每一個任務的模擬處理時間是1s,當再來的任務發現阻塞隊列中有任務還沒被取走,就建立非核心線程處理剛來的這個任務,不斷的來任務,不斷的建立線程,因此用這個阻塞隊列再搭配線程池的總線程數等參數設置可能會由於不斷的建立線程而致使OOM。
4.PriorityBlockingQueue 優先級隊列 進入隊列的元素會按照任務的優先級排序。而且必須實現Comparable接口。
參數:priorityTask - 要比較的對象。 返回:負整數、零或正整數, 根據此對象是小於、等於仍是大於指定對象(要比較的對象)。
先建立一個帶有優先級的任務
public class PriorityTask implements Runnable , Comparable<PriorityTask>{ private String taskName; // 優先級,根據這個數進行排序 private Integer priority; public PriorityTask(Integer priority,String taskName) { this.priority = priority; this.taskName = taskName; } //這個compareTo方法的返回值若是是-1的話,則排序會認爲傳過來的任務比此任務的大,降序排列 //這個compareTo方法的返回值若是是1的話,則排序會認爲傳過來的任務比此任務的小,升序排列 @Override public int compareTo(PriorityTask priorityTask) { //Integer.compare返回 -1表明 傳過來的任務的priority 比次任務的priority要小 // Integer.compare 0 傳過來的任務的priority 比次任務的priority同樣大 //Integer.compare 1 傳過來的任務的priority 比次任務的priority要大 return Integer.compare(priorityTask.priority,this.priority); } @Override public void run() { try { //模擬每一個任務的耗時 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String name = Thread.currentThread().getName(); System.out.println("這裏是xhJaver,線程池系列 當前線程名字是 " + name+" 處理了 "+ taskName+" 任務"); } @Override public String toString() { return "Task{" + "taskName='" + taskName + ''' + '}'; } }
Integer.compare 的 比較大小代碼
java public static int compare(int x, int y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); }
測試代碼
public static void main(String[] args) { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, new PriorityBlockingQueue()); for (int i=0;i<5;i++){ //建立十個任務 PriorityTask priorityTask = new PriorityTask(i,"task" + i); //去跑任務 threadPoolExecutor.execute(priorityTask); } for (int i=100;i>=95;i--){ //建立十個任務 PriorityTask priorityTask = new PriorityTask(i,"task" + i); //去跑任務 threadPoolExecutor.execute(priorityTask); } //記得要關閉線程池 threadPoolExecutor.shutdown(); }
輸出結果是
這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task0 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task100 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task99 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task98 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task97 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task96 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task95 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task4 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task3 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task2 任務 這裏是xhJaver,線程池系列 當前線程名字是 pool-1-thread-1 處理了 task1 任務
由輸出結果可見,除了第一個之外,處理任務的順序會按照優先級大小先處理
他們分別是如下幾種
1.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
有此可見,這個FixedThreadPool線程池的核心線程數和最大線程數同樣,因此就沒有非核心線程數,存活時間這個參數也就是無效的了,它底層用的是LinkedBlockingQueue這個阻塞隊列,這個隊列是個無界隊列,能夠點進去源碼看它默認的容量是Integer.MAX_VALUE
public LinkedBlockingQueue() { this(Integer.MAX_VALUE); }
因此這會致使一個什麼問題呢?就會致使,當核心線程都跑滿的時候,再來新任務的話就會不斷的添加至這個阻塞隊列裏面,一直加一直加,可是內存是有限的,因此有可能會出現 OOM(OutOfMemory) 的問題
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); }
2.newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
因而可知,它的核心線程數默認是0,線程池總線程容量是Integer.MAX_VALUE,阻塞隊列用的是SynchronousQueue同步隊列,非核心線程數的空閒存活時間爲60s,這會致使一個什麼問題呢?只要來了一個任務,若是沒有線程的話就建立一個非核心線程去跑這個任務,若是跑着的過程當中又來了一個任務,就會繼續建立線程去跑,以此類推,內存是有限的,不斷的建立線程的話也會觸發OOM問題
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }
3.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
咱們能夠看出,它的核心線程數是一個,總線程數也是一個。底層用的是LinkedBlockingQueue阻塞隊列 當來任務的時候線程池若是沒有線程的話,則建立一個也是惟一一個線程來執行任務,剩下的任務都會被塞進無界阻塞隊列裏面,也是會有可能產生OOM問題。
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }
什麼?線程池還能夠拓展?!是的,若是我想記錄下每一個任務的執行開始狀況,結束狀況,線程池關閉狀況就要拓展啦,ThreadPoolExecutor它內部是提供了幾個方法給咱們拓展,其中beforeExecute、afterExecute、terminated,這三個分別對應任務開始,任務結束,線程池關閉的三種狀況,因此咱們就要重寫他們啦,話很少說,看下代碼
public static void main(String[] args) { //阻塞隊列,設置阻塞任務最多爲10個 BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable> (10); //建立線程安全的計數器 AtomicInteger atomicInteger = new AtomicInteger(); //自定義線程工廠 ThreadFactory threadFactory = new DIYThreadFactory(atomicInteger); //自定義拒絕策略 當線程池的最大工做線程跑滿以及阻塞隊列滿了的話,會由拒絕策略處理剩下的任務 DIYRejectedHandler diyRejectedHandler = new DIYRejectedHandler(); //建立線程池 核心線程數爲5 最大線程數爲10 非核心線程空閒存活時間爲60s ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, blockingQueue, threadFactory, diyRejectedHandler ){ @Override protected void beforeExecute(Thread t, Runnable r) { System.out.println("xhJaver 當前線程是"+t.getName()+"開始處理任務:"+r.toString()); } @Override protected void afterExecute(Runnable r, Throwable t) { if(t!=null){ System.out.println("xhJaver 當前線程是"+Thread.currentThread().getName() +"處理任務結束:"+r.toString()+" 錯誤是 "+ t); } System.out.println("xhJaver 當前線程是"+Thread.currentThread().getName() +"處理任務結束:"+r.toString()+" 沒有錯誤 "); } @Override protected void terminated() { System.out.println("xhJaver 當前線程是"+Thread.currentThread().getName() +"關閉線程池"); } }; for (int i=0;i<21;i++){ //建立10個任務,若是要是建立>20個任務,則20之外的任務會交由拒絕策略處理 Task task = new Task("task" + i); //讓咱們自定義的線程池去跑這些任務 threadPoolExecutor.execute(task); } //記得要關閉線程池 threadPoolExecutor.shutdown(); }
輸出結果是
這是xhJaver沒法處理的任務 Task{taskName='task20'} 當前線程名字是 main xhJaver 當前線程是xhJaver-thread-7開始處理任務:Task{taskName='task17'} xhJaver 當前線程是xhJaver-thread-6開始處理任務:Task{taskName='task16'} xhJaver 當前線程是xhJaver-thread-9開始處理任務:Task{taskName='task19'} xhJaver 當前線程是xhJaver-thread-4開始處理任務:Task{taskName='task4'} xhJaver 當前線程是xhJaver-thread-8開始處理任務:Task{taskName='task18'} xhJaver 當前線程是xhJaver-thread-2開始處理任務:Task{taskName='task2'} xhJaver 當前線程是xhJaver-thread-3開始處理任務:Task{taskName='task3'} xhJaver 當前線程是xhJaver-thread-5開始處理任務:Task{taskName='task15'} xhJaver 當前線程是xhJaver-thread-0開始處理任務:Task{taskName='task0'} xhJaver 當前線程是xhJaver-thread-1開始處理任務:Task{taskName='task1'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-4 處理了 task4 任務 xhJaver 當前線程是xhJaver-thread-4處理任務結束:Task{taskName='task4'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-4開始處理任務:Task{taskName='task5'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-9 處理了 task19 任務 xhJaver 當前線程是xhJaver-thread-9處理任務結束:Task{taskName='task19'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-6 處理了 task16 任務 xhJaver 當前線程是xhJaver-thread-6處理任務結束:Task{taskName='task16'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-9開始處理任務:Task{taskName='task6'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-7 處理了 task17 任務 xhJaver 當前線程是xhJaver-thread-7處理任務結束:Task{taskName='task17'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-7開始處理任務:Task{taskName='task8'} xhJaver 當前線程是xhJaver-thread-6開始處理任務:Task{taskName='task7'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-1 處理了 task1 任務 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-8 處理了 task18 任務 xhJaver 當前線程是xhJaver-thread-8處理任務結束:Task{taskName='task18'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-8開始處理任務:Task{taskName='task9'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-2 處理了 task2 任務 xhJaver 當前線程是xhJaver-thread-2處理任務結束:Task{taskName='task2'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-2開始處理任務:Task{taskName='task10'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-3 處理了 task3 任務 xhJaver 當前線程是xhJaver-thread-3處理任務結束:Task{taskName='task3'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-5 處理了 task15 任務 xhJaver 當前線程是xhJaver-thread-5處理任務結束:Task{taskName='task15'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-5開始處理任務:Task{taskName='task12'} xhJaver 當前線程是xhJaver-thread-1處理任務結束:Task{taskName='task1'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-1開始處理任務:Task{taskName='task13'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-0 處理了 task0 任務 xhJaver 當前線程是xhJaver-thread-3開始處理任務:Task{taskName='task11'} xhJaver 當前線程是xhJaver-thread-0處理任務結束:Task{taskName='task0'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-0開始處理任務:Task{taskName='task14'} 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-4 處理了 task5 任務 xhJaver 當前線程是xhJaver-thread-4處理任務結束:Task{taskName='task5'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-6 處理了 task7 任務 xhJaver 當前線程是xhJaver-thread-6處理任務結束:Task{taskName='task7'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-9 處理了 task6 任務 xhJaver 當前線程是xhJaver-thread-9處理任務結束:Task{taskName='task6'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-7 處理了 task8 任務 xhJaver 當前線程是xhJaver-thread-7處理任務結束:Task{taskName='task8'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-2 處理了 task10 任務 xhJaver 當前線程是xhJaver-thread-2處理任務結束:Task{taskName='task10'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-8 處理了 task9 任務 xhJaver 當前線程是xhJaver-thread-8處理任務結束:Task{taskName='task9'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-5 處理了 task12 任務 xhJaver 當前線程是xhJaver-thread-5處理任務結束:Task{taskName='task12'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-0 處理了 task14 任務 xhJaver 當前線程是xhJaver-thread-0處理任務結束:Task{taskName='task14'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-3 處理了 task11 任務 xhJaver 當前線程是xhJaver-thread-3處理任務結束:Task{taskName='task11'} 沒有錯誤 這裏是xhJaver,線程池系列 當前線程名字是 xhJaver-thread-1 處理了 task13 任務 xhJaver 當前線程是xhJaver-thread-1處理任務結束:Task{taskName='task13'} 沒有錯誤 xhJaver 當前線程是xhJaver-thread-1關閉線程池
更多精彩請關注公衆號xhJaver,京東java工程師和你一塊兒成長