例子:
10年前單核CPU電腦,假的多線程,像馬戲團小丑玩多個球,CPU需要來回切換。
現在是多核電腦,多個線程各自跑在獨立的CPU上,不用切換效率高。
線程池的優勢:
線程池做的工作只要是控制運行的線程數量,處理過程中將任務放入隊列,然後在線程創建後啓動這些任務,如果線程數量超過了最大數量,超出數量的線程排隊等候,等其他線程執行完畢,再從隊列中取出任務來執行。
它的主要特點爲:線程複用;控制最大併發數;管理線程。
第一:降低資源消耗。通過重複利用已創建的線程降低線程創建和銷燬造成的銷耗。
第二:提高響應速度。當任務到達時,任務可以不需要等待線程創建就能立即執行。
第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會銷耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。
線程池常用的三個實現類
package main.test;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @Auther: wdq
* @Date: 2020/4/21 11:21
* @Description:
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executor1 = Executors.newFixedThreadPool(5);//一池5線程
ExecutorService executor2 = Executors.newSingleThreadExecutor();//一池單線程
ExecutorService executor3 = Executors.newCachedThreadPool();//一池N線程
// try {
// for (int i = 0; i < 10; i++) {
// executor1.execute(()->{
// System.out.println(Thread.currentThread().getName()+"辦理業務");
// });
// try { TimeUnit.SECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
// }
// }catch (Exception e){
// e.printStackTrace();
// }finally {
// executor1.shutdown();
// }
// try {
// for (int i = 0; i < 10; i++) {
// executor2.execute(()->{
// System.out.println(Thread.currentThread().getName()+"辦理業務");
// });
// }
// }catch (Exception e){
// e.printStackTrace();
// }finally {
// executor2.shutdown();
// }
try {
for (int i = 0; i < 10; i++) {
executor3.execute(()->{
System.out.println(Thread.currentThread().getName()+"辦理業務");
});
//休眠1毫秒
//try { TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
}
}catch (Exception e){
e.printStackTrace();
}finally {
executor3.shutdown();
}
}
}
三個實現類的底層分析
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
上面是三個實現類的源碼,可以看出,它們的底層實際上都是ThreadPoolExecutor
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的七大參數: corePoolSize:線程池中的常駐核心線程數;
maximumPoolSize:線程池中能夠容納同時執行的最大線程數,此值必須大於等於1;
keepAliveTime:多餘的空閒線程的存活時間,當前池中線程數量超過corePoolSize時,當空閒時間達到KeepAliveTime時,多餘線程會被銷燬直到剩下corPoolSize個線程爲止;
TimeUnit unit:keepAliveTime的單位;
BlockingQueue<Runnable> workQueue:任務隊列,被提交但尚未被執行的任務;
ThreadFactory threadFactory:表示生成線程池中工作線程的線程工廠,用於創建線程,一般默認的即可;
RejectedExecutionHandler handler:拒絕策略,表示當隊列滿了,並且工作線程大於等於線程池的最大線程數時,如何來拒絕請求執行的runnable的策略。
具體算法:
1、在創建了線程池後,開始等待請求。
2、當調用execute()方法添加一個請求任務時,線程池會做出如下判斷:
2.1如果正在運行的線程數量小於corePoolSize,那麼馬上創建線程運行這個任務;
2.2如果正在運行的線程數量大於或等於corePoolSize,那麼將這個任務放入隊列;
2.3如果這個時候隊列滿了且正在運行的線程數量還小於maximumPoolSize,那麼還是要創建非核心線程立刻運行這個任務;
2.4如果隊列滿了且正在運行的線程數量大於或等於maximumPoolSize,那麼線程池會啓動飽和拒絕策略來執行。
3、當一個線程完成任務時,它會從隊列中取下一個任務來執行。
4、當一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
如果當前運行的線程數大於corePoolSize,那麼這個線程就被停掉。
所以線程池的所有任務完成後,它最終會收縮到corePoolSize的大小。
自定義線程池
所以不允許使用Executes那三個系統提供的線程池,需要去自定義線程池。
package main.test;
import jdk.nashorn.internal.runtime.regexp.JoniRegExp;
import java.util.concurrent.*;
/**
* @Auther: wdq
* @Date: 2020/4/21 15:11
* @Description:
*/
public class DivThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
3,
7,
2,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 11; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"辦理業務");
});
//休眠1毫秒
//try { TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
}
}catch (Exception e){
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
上面定義的線程池,最大可接受10個請求(maximumPoolSize+LinkedBlockingQueue),如果超過10個請求,就會觸發拒絕機制。因爲自定義的觸發機制使用的是AbortPolicy(默認的),它會拋出異常RejectedExecutionException。
四種拒絕機制:
AbortPolicy:之間拋出RejectedExecutionException異常組織系統正常運行。
CallerRunsPolicy:調用者運行一種調節機制,該策略既不會拋棄任務,也不會拋出異常,而是回退給任務發出者。
DiscardOldestPolicy:拋棄隊列中等待最久的任務,然後把當前任務加到隊列中,嘗試再次提交當前任務。
DiscardPolicy:該策略默默丟棄無法處理的任務,不予任何處理也不拋異常。如果任務運行丟棄,這是最好的一種策略。
以上拒絕策略均實現了RejectedExecutionHandler。
如果加上休眠,看結果:
package main.test;
import jdk.nashorn.internal.runtime.regexp.JoniRegExp;
import java.util.concurrent.*;
/**
* @Auther: wdq
* @Date: 2020/4/21 15:11
* @Description:
*/
public class DivThreadPoolDemo {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
3,
7,
2,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 14; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"辦理業務");
});
//休眠1毫秒
try { TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ e.printStackTrace(); }
}
}catch (Exception e){
e.printStackTrace();
}finally {
executorService.shutdown();
}
}
}
/*
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
pool-1-thread-3辦理業務
pool-1-thread-1辦理業務
pool-1-thread-2辦理業務
*/