1、為什么要使用多線程
例子:
十年前單核CPU電腦,假的多線程,像馬戲團(tuán)玩多個球,其實(shí)在在頂端每時每刻也只有一顆,CPU需要來回切換
現(xiàn)在是多核電腦,多個線程各自跑在獨(dú)立的CPU上,不用切換,效率高
2、線程池的優(yōu)勢
線程池做的工作只要是控制運(yùn)行的線程數(shù)量,處理過程中將任務(wù)放入隊(duì)列,然后在線程創(chuàng)建后啟動這些任務(wù),如果線程數(shù)量超過了最大數(shù)量,超出數(shù)量的線程則排隊(duì)等候,等待其他線程執(zhí)行完畢,釋放資源,然后再從隊(duì)列中取出任務(wù)來執(zhí)行
主要特點(diǎn):
- 線程復(fù)用
- 降低資源消耗,通過重復(fù)利用已經(jīng)創(chuàng)建好的線程,從而降低線程創(chuàng)建和銷毀造成的消耗
- 控制最大并發(fā)數(shù)
- 提高響應(yīng)速度,當(dāng)任務(wù)到達(dá)時,任務(wù)可以不需要等待線程創(chuàng)建就能立即執(zhí)行
- 管理線程
- 提高線程的可管理性,線程是稀缺資源,如果無限制的創(chuàng)建,不僅僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,起到調(diào)優(yōu)和監(jiān)控的功能
3、Executor框架
Java中的線程池是通過Executor框架實(shí)現(xiàn)的,該框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor這幾個類

4、線程池七大參數(shù)
(1)corePoolSize:線程池中常駐核心線程數(shù)
(2)maximumPoolSize:線程池能夠容納同時執(zhí)行的最大線程數(shù),此值必須大于等于1
(3)keepAliveTime:多余的空閑線程存活時間。當(dāng)前線程池?cái)?shù)量超過corePoolSize時,當(dāng)空閑時間到達(dá)keepAliveTime值時,多余空閑線程會被銷毀直到只剩下corePoolSize個線程為止。
(4)unit:keepAliveTime的時間單位
(5)workQueue:任務(wù)隊(duì)列,被提交但尚未執(zhí)行的任務(wù)
(6)threadFactory:表示生成線程池中的工作線程的線程工廠,用于創(chuàng)建線程,一般為默認(rèn)線程工廠即可
(7)handler:拒絕策略,表示當(dāng)隊(duì)列滿了并且工作線程大于等于線程池的最大線程數(shù)(maximumPoolSize)時如何來拒絕來請求的Runnable的策略
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
5、線程池的使用
5.1、Executors.newFixedThreadPool(int)
- 執(zhí)行長期任務(wù)性能好,創(chuàng)建一個線程池,
一池有N個固定的線程,有固定線程數(shù)的線程
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newFixedThreadPool創(chuàng)建的線程池corePoolSize和maximumPoolSize值是相等的,它使用的是LinkedBlockingQueue
5.2、Executors.newSingleThreadExecutor()
- 一個任務(wù)一個任務(wù)的執(zhí)行,一池一線程
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- newSingleThreadExecutor 創(chuàng)建的線程池corePoolSize和maximumPoolSize值都是1,它使用的是LinkedBlockingQueue
5.3、Executors.newCachedThreadPool()
- 執(zhí)行很多短期異步任務(wù),線程池根據(jù)需要創(chuàng)建新線程,
但在先前構(gòu)建的線程可用時將重用它們??蓴U(kuò)容,遇強(qiáng)則強(qiáng)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- newCachedThreadPool創(chuàng)建的線程池將corePoolSize設(shè)置為0,將maximumPoolSize設(shè)置為Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是說來了任務(wù)就創(chuàng)建線程運(yùn)行,當(dāng)線程空閑超過60秒,就銷毀線程。
/**
* 線程池
* Arrays
* Collections
* Executors
*/
public class MyThreadPoolDemo {
public static void main(String[] args) {
//固定數(shù)的線程池,一池五線程
// ExecutorService threadPool = Executors.newFixedThreadPool(5); //一個銀行網(wǎng)點(diǎn),5個受理業(yè)務(wù)的窗口
// ExecutorService threadPool = Executors.newSingleThreadExecutor(); //一個銀行網(wǎng)點(diǎn),1個受理業(yè)務(wù)的窗口
ExecutorService threadPool = Executors.newCachedThreadPool(); //一個銀行網(wǎng)點(diǎn),可擴(kuò)展受理業(yè)務(wù)的窗口
//10個顧客請求
try {
for (int i = 1; i <=10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 辦理業(yè)務(wù)");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}

6、線程池底層原理


流程
1、在創(chuàng)建了線程池后,開始等待請求。
2、當(dāng)調(diào)用execute()方法添加一個請求任務(wù)時,線程池會做出如下判斷:
2.1如果正在運(yùn)行的線程數(shù)量小于corePoolSize,那么馬上創(chuàng)建線程運(yùn)行這個任務(wù);
2.2如果正在運(yùn)行的線程數(shù)量大于或等于corePoolSize,那么將這個任務(wù)放入隊(duì)列;
2.3如果這個時候隊(duì)列滿了且正在運(yùn)行的線程數(shù)量還小于maximumPoolSize,那么還是要創(chuàng)建非核心線程立刻運(yùn)行這個任務(wù);
2.4如果隊(duì)列滿了且正在運(yùn)行的線程數(shù)量大于或等于maximumPoolSize,那么線程池會啟動飽和拒絕策略來執(zhí)行。
3、當(dāng)一個線程完成任務(wù)時,它會從隊(duì)列中取下一個任務(wù)來執(zhí)行。
4、當(dāng)一個線程無事可做超過一定的時間(keepAliveTime)時,線程會判斷:
如果當(dāng)前運(yùn)行的線程數(shù)大于corePoolSize,那么這個線程就被停掉。
所以線程池的所有任務(wù)完成后,它最終會收縮到corePoolSize的大小。
7、線程池的拒絕策略
拒絕策略
- 等待隊(duì)列已經(jīng)排滿了,再也塞不下新任務(wù)了
- 同時,線程池中的max線程也達(dá)到了,無法繼續(xù)為 新任務(wù)服務(wù)
- 這個時候就需要拒絕策略機(jī)制合理的處理這個問題
JDK內(nèi)置的拒絕策略
以下策略均實(shí)現(xiàn)RejectedExecutionHandle接口
- AbortPolicy(默認(rèn)):直接拋出RejectedExecutionException異常阻止系統(tǒng)正常運(yùn)行
- CallerRunsPolicy:“調(diào)用者運(yùn)行”一種調(diào)節(jié)機(jī)制,該策略既不會拋棄任務(wù),也不
會拋出異常,而是將某些任務(wù)回退到調(diào)用者,從而降低新任務(wù)的流量。 - DiscardOldestPolicy:拋棄隊(duì)列中等待最久的任務(wù),然后把當(dāng)前任務(wù)加人隊(duì)列中
嘗試再次提交當(dāng)前任務(wù)。 - DiscardPolicy:該策略默默地丟棄無法處理的任務(wù),不予任何處理也不拋出異常。
如果允許任務(wù)丟失,這是最好的一種策略。
8、日常開發(fā)中并不會去使用系統(tǒng)提供的創(chuàng)建線程池的方法

9、自定義線程池
public class MyThreadPoolDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy()
//new ThreadPoolExecutor.CallerRunsPolicy()
//new ThreadPoolExecutor.DiscardOldestPolicy()
new ThreadPoolExecutor.DiscardOldestPolicy()
);
//10個顧客請求
try {
for (int i = 1; i <= 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 辦理業(yè)務(wù)");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}