JUC并发编程学习笔记(十)线程池(重点)
線程池(重點(diǎn))
線程池:三大方法、七大參數(shù)、四種拒絕策略
池化技術(shù)
程序的運(yùn)行,本質(zhì):占用系統(tǒng)的資源!優(yōu)化資源的使用!-> 池化技術(shù)(線程池、連接池、對(duì)象池......);創(chuàng)建和銷毀十分消耗資源
池化技術(shù):事先準(zhǔn)備好一些資源,有人要用就拿,拿完用完還給我。
線程池的好處:
1、降低資源消耗
2、提高相應(yīng)速度
3、方便管理
線程復(fù)用、可以控制最大并發(fā)數(shù)、管理線程
線程池:三大方法
1、newSingleThreadExecutor
單列線程池,只有一條線程;
單例線程池配合callable使用,注意需要在程序運(yùn)行結(jié)束后關(guān)閉線程池
package org.example.pool;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//Executors->工具類
public class Demo01 {
public static void main(String[] args) {
TestCallable able = new TestCallable();
//三大方法
ExecutorService threadPool = Executors.newSingleThreadExecutor();//單個(gè)線程
try {
for (int i = 0; i < 10; i++) {
FutureTask<String> task = new FutureTask<String>(able);
//線程池創(chuàng)建線程
threadPool.execute(task);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//線程池用完,程序結(jié)束,關(guān)閉線程池
threadPool.shutdown();
}
// Executors.newFixedThreadPool(5);//固定線程池大小
// Executors.newCachedThreadPool();//可伸縮的線程池
}
}
class TestCallable implements Callable<String> {
Lock lock = new ReentrantLock();
@Override
public String call() throws Exception {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + ":ok");
return Thread.currentThread().getName() + ":ok";
}
}
注意結(jié)果圖
在單例線程池中,運(yùn)行結(jié)果發(fā)現(xiàn)都是同一條線程在操作資源,整個(gè)線程池中只有一條pool-1-thread-1線程。
2、newFixedThreadPool
同樣的代碼,將線程池切換為固定大小的線程池,設(shè)置為5條,這樣跑出來(lái)的結(jié)果又不一樣
由于設(shè)置了線程休眠,所以會(huì)導(dǎo)致比較平均的結(jié)果出現(xiàn),但是一般情況下都是五條線程搶占資源,每次結(jié)果都是不一定的,看那條線程處理的比較快搶占的比較多。
3、newCachedThreadPool
同樣的代碼,將線程池切換為可伸縮大小的線程池,這樣跑出來(lái)的結(jié)果又不一樣
根據(jù)業(yè)務(wù)代碼生成具體條數(shù)的線程:如本次業(yè)務(wù)通過(guò)循環(huán)或其他因數(shù),同時(shí)需要處理10條任務(wù),那么當(dāng)你線程池中的第一條線程還未完成任務(wù)時(shí)就會(huì)生成一條新的線程來(lái)同步處理這些任務(wù),只要你cpu處理速度夠快那么理論最高可能同時(shí)生成一個(gè)具有10條線程(任務(wù)數(shù))的一個(gè)線程池。
七大參數(shù)
源碼分析
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
//和以上沒(méi)有區(qū)別,只是通過(guò)用戶調(diào)用來(lái)完成的,相當(dāng)于new ThreadPoolExecutor(5, 5,....)
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
//設(shè)置了默認(rèn)是0個(gè)線程,但是最大值可以達(dá)到大約21億條,設(shè)置了Integer.MAX_VALUE(約21億)
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//如果通過(guò)Integer.MAX_VALUE來(lái)跑線程池一定會(huì)照成OOM(溢出)
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
通過(guò)觀察以上三大方法的創(chuàng)建線程池方式,可以發(fā)現(xiàn),三大方法的本質(zhì)都是調(diào)用ThreadPoolExecutor來(lái)創(chuàng)建的
public ThreadPoolExecutor(int corePoolSize,//核心線程池大小
int maximumPoolSize,//最大線程池大小
long keepAliveTime,//超時(shí)無(wú)調(diào)用釋放
TimeUnit unit,//超時(shí)單位
BlockingQueue<Runnable> workQueue,//阻塞隊(duì)列
ThreadFactory threadFactory,//線程工廠,創(chuàng)建線程的,一般不用動(dòng)
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;
}
這就是為什么阿里巴巴開(kāi)發(fā)規(guī)范中說(shuō)不要使用Executors來(lái)創(chuàng)建線程池而是讓我們通過(guò)ThreadPoolExecutor來(lái)創(chuàng)建,其實(shí)就是讓我們通過(guò)了解線程池的本質(zhì)來(lái)避免一些問(wèn)題。
模擬銀行業(yè)務(wù)模塊模擬
package org.example.pool;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo02 {
public static void main(String[] args) {
//自定義線程池,工作中只會(huì)使用ThreadPoolExecutor
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
new ThreadPoolExecutor.AbortPolicy());//拒絕策略,即當(dāng)阻塞隊(duì)列和線程池線程都已經(jīng)最大化運(yùn)行沒(méi)有任何位置可以處置接下來(lái)的元素了,就拒絕該元素進(jìn)入并拋出異常
try {
//最大承載 隊(duì)列Queue+max
for (int i = 0; i < 10; i++) {
//線程池創(chuàng)建線程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + ":ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//線程池用完,程序結(jié)束,關(guān)閉線程池
threadPool.shutdown();
}
}
}
此時(shí)有10個(gè)任務(wù),但是最大線程量只有5個(gè),阻塞隊(duì)列量只有三個(gè),所以注定會(huì)有兩個(gè)無(wú)法處理,此時(shí)觸發(fā)拒絕策略,拋出異常并且拒絕該任務(wù)。
可以看到執(zhí)行了八個(gè)任務(wù),通過(guò)五條不同的線程。執(zhí)行了拒絕策略拋出了異常java.util.concurrent.RejectedExecutionException
四種拒絕策略
四種拒絕策略的描述
//AbortPolicy 隊(duì)列滿了,丟掉任務(wù),拋出異常
//CallerRunsPolicy 哪條線程給的任務(wù)回到哪條線程去執(zhí)行,線程池不執(zhí)行
//DiscardPolicy 隊(duì)列滿了,丟掉任務(wù),但不拋出異常
//DiscardOldestPolicy 隊(duì)列滿了,嘗試去和最早的競(jìng)爭(zhēng),如果沒(méi)成功,依舊丟棄任務(wù),但不拋出異常
小結(jié)和拓展
了解:IO密集型、cpu密集型(調(diào)優(yōu))
/*
* 最大線程到底該如何定義
* 1、CPU 密集型,幾何就定義為幾,就可以保證cpu效率最高的
* 2、IO 密集型 > 判斷你程序中十分耗IO的線程,
* 程序 15個(gè)大小任務(wù) io十分占用資源
* */
總結(jié)
以上是生活随笔為你收集整理的JUC并发编程学习笔记(十)线程池(重点)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: CSS必学:元素之间的空白与行内块的幽灵
- 下一篇: Go 方法集合与选择receiver类型