日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java io密集型任务_Java线程池讲解——针对IO密集型任务

發布時間:2024/9/19 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java io密集型任务_Java线程池讲解——针对IO密集型任务 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

sap java開發技術詳解&mdash基礎

94.01元

(需用券)

去購買 >

針對 IO 密集型的任務,我們可以針對原本的線程池做一些改造,從而可以提高任務的處理效率。

基本

在阿里巴巴泰山版java開發手冊中有這么一條:

線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,

這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。

那么如果要使用 ThreadPoolExecutor ,那就先來看看構造方法中的所有入參:

corePoolSize : 核心線程數,當線程池中的線程數量為 corePoolSize 時,即使這些線程處于空閑狀態,也不會銷毀(除非設置 allowCoreThreadTimeOut)。

maximumPoolSize : 最大線程數,線程池中允許的線程數量的最大值。

keepAliveTime : 線程空閑時間,當線程池中的線程數大于 corePoolSize 時,多余的空閑線程將在銷毀之前等待新任務的最長時間。

workQueue : 任務隊列

unit : 線程空閑時間的單位。

threadFactory : 線程工廠,線程池創建線程時使用的工廠。

handler : 拒絕策略,因達到線程邊界和任務隊列滿時,針對新任務的處理方法。

這么說可能有些難以理解,你可以結合下圖進行參考:

那么由此我們可以知道,當大量任務被放入線程池之后,先是被核心線程執行,多余的會被放進隊列里,當隊列滿了之后才會創建額外的線程進行處理,再多就會采取拒絕策略。

但這樣真的能滿足我們的所有需求嗎?

任務的分類

正常來說,我們可以把需要處理的任務按照消耗資源的不同,分為兩種:CPU 密集型和IO 密集型。

CPU 密集型

既然名字里帶有CPU了,說明其消耗的主要資源就是 CPU 了。

具體是指那種包含大量運算、在持有的 CPU 分配的時間片上一直在執行任務、幾乎不需要依賴或等待其他任何東西。

這樣的任務,在我的理解中,處理起來其實沒有多少優化空間,因為處理時幾乎沒有等待時間,所以一直占有 CPU 進行執行,才是最好的方式。

唯一能想到優化的地方,就是當單個線程累計較多任務時,其他線程能進行分擔,類似fork/join框架的概念。

設置線程數時,針對單臺機器,最好就是有幾個 CPU ,就創建幾個線程,然后每個線程都在執行這種任務,永不停歇。

IO 密集型

和上面一樣,既然名字里帶有IO了,說明其消耗的主要資源就是 IO 了。

我們所接觸到的 IO ,大致可以分成兩種:磁盤 IO和網絡 IO。

磁盤 IO ,大多都是一些針對磁盤的讀寫操作,最常見的就是文件的讀寫,假如你的數據庫、 Redis 也是在本地的話,那么這個也屬于磁盤 IO。

網絡 IO ,這個應該是大家更加熟悉的,我們會遇到各種網絡請求,比如 http 請求、遠程數據庫讀寫、遠程 Redis 讀寫等等。

IO 操作的特點就是需要等待,我們請求一些數據,由對方將數據寫入緩沖區,在這段時間中,需要讀取數據的線程根本無事可做,因此可以把 CPU 時間片讓出去,直到緩沖區寫滿。

既然這樣,IO 密集型任務其實就有很大的優化空間了(畢竟存在等待),那現有的線程池可以很好的滿足我們的需求嗎?

線程池的優化

還記得上面說的, ThreadPoolExecutor 針對多余任務的處理,是先放到等待隊列中,當隊列塞滿后,再創建額外的線程進行處理。

假設我們的任務基本都是 IO 密集型,我們希望程序可以有更高的吞吐量,可以在更短的時間內處理更多的任務,那么上面的 ThreadPoolExecutor 明顯是不滿足我們的需求,那該如何解決呢?

也許再來看看 ThreadPoolExecutor 的 execute 方法,會讓我們有一些思路:

public void execute(Runnable command) {

if (command == null)

throw new NullPointerException();

int c = ctl.get();

// 如果當前活躍線程數,小于核心線程數

if (workerCountOf(c) < corePoolSize) {

// 則優先創建線程

if (addWorker(command, true))

return;

c = ctl.get();

}

// 如果任務可以成功放入隊列中

if (isRunning(c) && workQueue.offer(command)) {

int recheck = ctl.get();

if (! isRunning(recheck) && remove(command))

reject(command);

else if (workerCountOf(recheck) == 0)

addWorker(null, false);

}

// 如果不可以成功放入隊列,則創建線程

else if (!addWorker(command, false))

// 如果無法繼續創建線程,則拒絕任務

reject(command);

}

針對放入隊列的操作,如果隊列放入失敗,線程池就會選擇去創建線程了。因此,我們或許可以嘗試自定義線程池,針對 offer 操作,做一些自定義處理。

也就是將任務放入隊列時,先檢查線程池的線程數是否小于最大線程數,如果是,則拒絕放入隊列,否則,再嘗試放入隊列中。

如果你有看過 dubbo 或者 tomcat 的線程池,你會發現他們就有這樣的實現方法。

比如 dubbo 中的 TaskQueue,我們來看看它的 offer 方法:

@Override

public boolean offer(Runnable runnable) {

if (executor == null) {

throw new RejectedExecutionException("The task queue does not have executor!");

}

int currentPoolThreadSize = executor.getPoolSize();

// 如果有空閑等待的線程,則將任務放入隊列中,讓線程去處理任務

if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {

return super.offer(runnable);

}

// 如果當前線程數小于最大線程數,則返回 false ,讓線程池去創建新的線程

if (currentPoolThreadSize < executor.getMaximumPoolSize()) {

return false;

}

// 否則,就將任務放入隊列中

return super.offer(runnable);

}

這樣就可以讓線程池優先新建線程了。需要注意的時,此時的隊列因為需要根據線程池中的線程數決定是否放入任務成功,所以需要持有executor對象,這點不要忘記奧。

總結

通過本篇文章,主要是讓大家重新了解了一下 ThreadPoolExecutor ,并針對高吞吐場景下如何進行局部優化。

有興趣的話可以訪問我的博客或者關注我的公眾號、頭條號,說不定會有意外的驚喜。

https://death00.github.io/

公眾號:健程之道

java 11官方入門(第8版)教材

79.84元

包郵

(需用券)

去購買 >

總結

以上是生活随笔為你收集整理的java io密集型任务_Java线程池讲解——针对IO密集型任务的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。