最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

了解Java線程池創(chuàng)建過程

 更新時間:2019年05月31日 14:05:32   作者:展翅而飛  
那么有沒有一種辦法使得線程可以復(fù)用,就是執(zhí)行完一個任務(wù),并不被銷毀,而是可以繼續(xù)執(zhí)行其他的任務(wù)?在Java中可以通過線程池來達(dá)到這樣的效果。下面我們來詳細(xì)了解一下吧

前言

最近在改進(jìn)項目的并發(fā)功能,但開發(fā)起來磕磕碰碰的。看了好多資料,總算加深了認(rèn)識。于是打算配合查看源代碼,總結(jié)并發(fā)編程的原理。

準(zhǔn)備從用得最多的線程池開始,圍繞創(chuàng)建、執(zhí)行、關(guān)閉認(rèn)識線程池整個生命周期的實(shí)現(xiàn)原理。后續(xù)再研究原子變量、并發(fā)容器、阻塞隊列、同步工具、鎖等等主題。java.util.concurrent里的并發(fā)工具用起來不難,但不能僅僅會用,我們要read the fucking source code,哈哈。順便說聲,我用的JDK是1.8。

Executor框架

Executor是一套線程池管理框架,接口里只有一個方法execute,執(zhí)行Runnable任務(wù)。ExecutorService接口擴(kuò)展了Executor,添加了線程生命周期的管理,提供任務(wù)終止、返回任務(wù)結(jié)果等方法。AbstractExecutorService實(shí)現(xiàn)了ExecutorService,提供例如submit方法的默認(rèn)實(shí)現(xiàn)邏輯。

然后到今天的主題ThreadPoolExecutor,繼承了AbstractExecutorService,提供線程池的具體實(shí)現(xiàn)。

構(gòu)造方法

下面是ThreadPoolExecutor最普通的構(gòu)造函數(shù),最多有七個參數(shù)。具體代碼不貼了,只是一些參數(shù)校驗(yàn)和設(shè)置的語句。

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}

corePoolSize是線程池的目標(biāo)大小,即是線程池剛剛創(chuàng)建起來,還沒有任務(wù)要執(zhí)行時的大小。maximumPoolSize是線程池的最大上限。keepAliveTime是線程的存活時間,當(dāng)線程池內(nèi)的線程數(shù)量大于corePoolSize,超出存活時間的空閑線程就會被回收。unit就不用說了,剩下的三個參數(shù)看后文的分析。

預(yù)設(shè)的定制線程池

ThreadPoolExecutor預(yù)設(shè)了一些已經(jīng)定制好的線程池,由Executors里的工廠方法創(chuàng)建。下面分析newSingleThreadExecutor、newFixedThreadPool、newCachedThreadPool的創(chuàng)建參數(shù)。

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

newFixedThreadPool的corePoolSize和maximumPoolSize都設(shè)置為傳入的固定數(shù)量,keepAliveTim設(shè)置為0。線程池創(chuàng)建后,線程數(shù)量將會固定不變,適合需要線程很穩(wěn)定的場合。

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

newSingleThreadExecutor是線程數(shù)量固定為1的newFixedThreadPool版本,保證池內(nèi)的任務(wù)串行。注意到返回的是FinalizableDelegatedExecutorService,來看看源碼:

static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}

FinalizableDelegatedExecutorService繼承了DelegatedExecutorService,僅僅在gc時增加關(guān)閉線程池的操作,再來看看DelegatedExecutorService的源碼:

static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
public List<Runnable> shutdownNow() { return e.shutdownNow(); }
public boolean isShutdown() { return e.isShutdown(); }
public boolean isTerminated() { return e.isTerminated(); }
//...
}


代碼很簡單,DelegatedExecutorService包裝了ExecutorService,使其只暴露出ExecutorService的方法,因此不能再配置線程池的參數(shù)。本來,線程池創(chuàng)建的參數(shù)是可以調(diào)整的,ThreadPoolExecutor提供了set方法。使用newSingleThreadExecutor目的是生成單線程串行的線程池,如果還能配置線程池大小,那就沒意思了。

Executors還提供了unconfigurableExecutorService方法,將普通線程池包裝成不可配置的線程池。如果不想線程池被不明所以的后人修改,可以調(diào)用這個方法。

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

newCachedThreadPool生成一個會緩存的線程池,線程數(shù)量可以從0到Integer.MAX_VALUE,超時時間為1分鐘。線程池用起來的效果是:如果有空閑線程,會復(fù)用線程;如果沒有空閑線程,會新建線程;如果線程空閑超過1分鐘,將會被回收。

newScheduledThreadPool

newScheduledThreadPool將會創(chuàng)建一個可定時執(zhí)行任務(wù)的線程池。這個不打算在本文展開,后續(xù)會另開文章細(xì)講。

等待隊列

newCachedThreadPool的線程上限幾乎等同于無限,但系統(tǒng)資源是有限的,任務(wù)的處理速度總有可能比不上任務(wù)的提交速度。因此,可以為ThreadPoolExecutor提供一個阻塞隊列來保存因線程不足而等待的Runnable任務(wù),這就是BlockingQueue。

JDK為BlockingQueue提供了幾種實(shí)現(xiàn)方式,常用的有:

  • ArrayBlockingQueue:數(shù)組結(jié)構(gòu)的阻塞隊列
  • LinkedBlockingQueue:鏈表結(jié)構(gòu)的阻塞隊列
  • PriorityBlockingQueue:有優(yōu)先級的阻塞隊列
  • SynchronousQueue:不會存儲元素的阻塞隊列

newFixedThreadPool和newSingleThreadExecutor在默認(rèn)情況下使用一個無界的LinkedBlockingQueue。要注意的是,如果任務(wù)一直提交,但線程池又不能及時處理,等待隊列將會無限制地加長,系統(tǒng)資源總會有消耗殆盡的一刻。所以,推薦使用有界的等待隊列,避免資源耗盡。但解決一個問題,又會帶來新問題:隊列填滿之后,再來新任務(wù),這個時候怎么辦?后文會介紹如何處理隊列飽和。

newCachedThreadPool使用的SynchronousQueue十分有趣,看名稱是個隊列,但它卻不能存儲元素。要將一個任務(wù)放進(jìn)隊列,必須有另一個線程去接收這個任務(wù),一個進(jìn)就有一個出,隊列不會存儲任何東西。因此,SynchronousQueue是一種移交機(jī)制,不能算是隊列。newCachedThreadPool生成的是一個沒有上限的線程池,理論上提交多少任務(wù)都可以,使用SynchronousQueue作為等待隊列正合適。

飽和策略

當(dāng)有界的等待隊列滿了之后,就需要用到飽和策略去處理,ThreadPoolExecutor的飽和策略通過傳入RejectedExecutionHandler來實(shí)現(xiàn)。如果沒有為構(gòu)造函數(shù)傳入,將會使用默認(rèn)的defaultHandler。

private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
}

AbortPolicy是默認(rèn)的實(shí)現(xiàn),直接拋出一個RejectedExecutionException異常,讓調(diào)用者自己處理。除此之外,還有幾種飽和策略,來看一下:

public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}

DiscardPolicy的rejectedExecution直接是空方法,什么也不干。如果隊列滿了,后續(xù)的任務(wù)都拋棄掉。

public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}


DiscardOldestPolicy會將等待隊列里最舊的任務(wù)踢走,讓新任務(wù)得以執(zhí)行。

public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}

最后一種飽和策略是CallerRunsPolicy,它既不拋棄新任務(wù),也不拋棄舊任務(wù),而是直接在當(dāng)前線程運(yùn)行這個任務(wù)。當(dāng)前線程一般就是主線程啊,讓主線程運(yùn)行任務(wù),說不定就阻塞了。如果不是想清楚了整套方案,還是少用這種策略為妙。

ThreadFactory

每當(dāng)線程池需要創(chuàng)建一個新線程,都是通過線程工廠獲取。如果不為ThreadPoolExecutor設(shè)定一個線程工廠,就會使用默認(rèn)的defaultThreadFactory:

public static ThreadFactory defaultThreadFactory() {
return new DefaultThreadFactory();
}
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}

平時打印線程池里線程的name時,會輸出形如pool-1-thread-1之類的名稱,就是在這里設(shè)置的。這個默認(rèn)的線程工廠,創(chuàng)建的線程是普通的非守護(hù)線程,如果需要定制,實(shí)現(xiàn)ThreadFactory后傳給ThreadPoolExecutor即可。

不看代碼不總結(jié)不會知道,光是線程池的創(chuàng)建就可以引出很多學(xué)問。別看平時創(chuàng)建線程池是一句代碼的事,其實(shí)ThreadPoolExecutor提供了很靈活的定制方法。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Spring中@PathVariable注解的簡單使用

    Spring中@PathVariable注解的簡單使用

    這篇文章主要介紹了Spring中@PathVariable注解的簡單使用,@PathVariable 是 Spring Framework 中的注解之一,用于處理 RESTful Web 服務(wù)中的 URL 路徑參數(shù),它的作用是將 URL 中的路徑變量綁定到方法的參數(shù)上,需要的朋友可以參考下
    2024-01-01
  • 解決get請求入?yún)NotNull驗(yàn)證不生效問題

    解決get請求入?yún)NotNull驗(yàn)證不生效問題

    這篇文章主要介紹了解決get請求入?yún)NotNull驗(yàn)證不生效問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 關(guān)于SpringBoot打包測試、生產(chǎn)環(huán)境方式

    關(guān)于SpringBoot打包測試、生產(chǎn)環(huán)境方式

    這篇文章主要介紹了關(guān)于SpringBoot打包測試、生產(chǎn)環(huán)境方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • 簡單了解java中靜態(tài)初始化塊的執(zhí)行順序

    簡單了解java中靜態(tài)初始化塊的執(zhí)行順序

    這篇文章主要介紹了簡單了解java中靜態(tài)初始化塊的執(zhí)行順序,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10
  • JAVA 內(nèi)存溢出案例匯總

    JAVA 內(nèi)存溢出案例匯總

    這篇文章主要介紹了JAVA 內(nèi)存溢出案例的匯總,文中講解非常細(xì)致,幫助各位工作學(xué)習(xí)時避免內(nèi)存溢出,感興趣的朋友可以了解下
    2020-07-07
  • SpringBoot如何整合SpringDataJPA

    SpringBoot如何整合SpringDataJPA

    這篇文章主要介紹了SpringBoot整合SpringDataJPA代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Java中的synchronized關(guān)鍵字

    Java中的synchronized關(guān)鍵字

    這篇文章主要介紹了Java中的synchronized關(guān)鍵字,synchronized可以保證方法或代碼塊在運(yùn)行時,同一時刻只有一個線程可以進(jìn)入到臨界區(qū)(互斥性),同時它還保證了共享變量的內(nèi)存可見性,下面我們就來看看你文章對synchronized鎖的介紹,需要的朋友也可以參考一下
    2021-12-12
  • Java 對接飛書多維表格使用詳解(微服務(wù))

    Java 對接飛書多維表格使用詳解(微服務(wù))

    本文詳細(xì)介紹了如何基于飛書開放平臺在微服務(wù)項目中操作飛書多維表格,包括應(yīng)用創(chuàng)建、授權(quán)、多維表數(shù)據(jù)操作(新增、查詢、刪除)以及Java SDK實(shí)現(xiàn)等步驟,感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • java String類型對象轉(zhuǎn)換為自定義類型對象的實(shí)現(xiàn)

    java String類型對象轉(zhuǎn)換為自定義類型對象的實(shí)現(xiàn)

    本文主要介紹了java String類型對象轉(zhuǎn)換為自定義類型對象的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 聊聊Java三種常見的分布式鎖

    聊聊Java三種常見的分布式鎖

    目前分布式鎖的實(shí)現(xiàn)方案主要包括三種,本文就來介紹一下這三種常見的分布式鎖以及這三種鎖的性能等,具有一定的參考價值,感興趣的可以了解一下
    2023-06-06

最新評論

河北区| 哈巴河县| 桦甸市| 石台县| 九江市| 彝良县| 平泉县| 辉县市| 沙坪坝区| 甘德县| 明水县| 宣武区| 大冶市| 平武县| 浮梁县| 丹东市| 墨竹工卡县| 吉首市| 文昌市| 宣汉县| 武平县| 泗洪县| 米泉市| 宜黄县| 华亭县| 涟水县| 武定县| 上饶县| 贡觉县| 磴口县| 邓州市| 石台县| 绵竹市| 高尔夫| 靖边县| 和顺县| 永州市| 滨州市| 新乐市| 赤壁市| 凤阳县|