JAVA線程池原理詳解

線程池的優點

1、線程是稀缺資源,使用線程池可以減少創建和銷毀線程的次數,每個工作線程都可以重復使用。

2、可以根據系統的承受能力,調整線程池中工作線程的數量,防止因為消耗過多內存導致服務器崩潰。

線程池的創建

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

corePoolSize:線程池核心線程數量

maximumPoolSize:線程池最大線程數量

keepAliverTime:當活躍線程數大于核心線程數時,空閑的多余線程最大存活時間

unit:存活時間的單位

workQueue:存放任務的隊列

handler:超出線程范圍和隊列容量的任務的處理程序

線程池的實現原理

提交一個任務到線程池中,線程池的處理流程如下:

1、判斷線程池里的核心線程是否都在執行任務,如果不是(核心線程空閑或者還有核心線程沒有被創建)則創建一個新的工作線程來執行任務。如果核心線程都在執行任務,則進入下個流程。

2、線程池判斷工作隊列是否已滿,如果工作隊列沒有滿,則將新提交的任務存儲在這個工作隊列里。如果工作隊列滿了,則進入下個流程。

3、判斷線程池里的線程是否都處于工作狀態,如果沒有,則創建一個新的工作線程來執行任務。如果已經滿了,則交給飽和策略來處理這個任務。

線程池的源碼解讀

1、ThreadPoolExecutor的execute()方法

 public void execute(Runnable command) {if (command == null)throw new NullPointerException();       //如果線程數大于等于基本線程數或者線程創建失敗,將任務加入隊列if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {          //線程池處于運行狀態并且加入隊列成功if (runState == RUNNING && workQueue.offer(command)) {if (runState != RUNNING || poolSize == 0)ensureQueuedTaskHandled(command);}         //線程池不處于運行狀態或者加入隊列失敗,則創建線程(創建的是非核心線程)else if (!addIfUnderMaximumPoolSize(command))           //創建線程失敗,則采取阻塞處理的方式reject(command); // is shutdown or saturated}}

2、創建線程的方法:addIfUnderCorePoolSize(command)

  private boolean addIfUnderCorePoolSize(Runnable firstTask) {Thread t = null;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {if (poolSize < corePoolSize && runState == RUNNING)t = addThread(firstTask);} finally {mainLock.unlock();}if (t == null)return false;t.start();return true;}

主要關注點在 新增線程部分

1 private Thread addThread(Runnable firstTask) {2         Worker w = new Worker(firstTask);3         Thread t = threadFactory.newThread(w);4         if (t != null) {5             w.thread = t;6             workers.add(w);7             int nt = ++poolSize;8             if (nt > largestPoolSize)9                 largestPoolSize = nt;
10         }
11         return t;
12     }

這里將線程封裝成工作線程worker,并放入工作線程組里,worker類的方法run方法:

 public void run() {try {Runnable task = firstTask;firstTask = null;while (task != null || (task = getTask()) != null) {runTask(task);task = null;}} finally {workerDone(this);}}

worker在執行完任務后,還會通過getTask方法循環獲取工作隊里里的任務來執行。

RejetedExecutionHandler:飽和策略

當隊列和線程池都滿了,說明線程池處于飽和狀態,那么必須對新提交的任務采用一種特殊的策略來進行處理。這個策略默認配置是AbortPolicy,表示無法處理新的任務而拋出異常。JAVA提供了4中策略:

1、AbortPolicy:直接拋出異常

2、CallerRunsPolicy:只用調用所在的線程運行任務

3、DiscardOldestPolicy:丟棄隊列里最近的一個任務,并執行當前任務。

4、DiscardPolicy:不處理,丟棄掉。

Executor框架的兩級調度模型

在HotSpot VM的模型中,JAVA線程被一對一映射為本地操作系統線程。JAVA線程啟動時會創建一個本地操作系統線程,當JAVA線程終止時,對應的操作系統線程也被銷毀回收,而操作系統會調度所有線程并將它們分配給可用的CPU。

在上層,JAVA程序會將應用分解為多個任務,然后使用應用級的調度器(Executor)將這些任務映射成固定數量的線程;在底層,操作系統內核將這些線程映射到硬件處理器上。

Executor框架類圖

JAVA線程既是工作單元,也是執行機制。而在Executor框架中,我們將工作單元與執行機制分離開來。Runnable和Callable是工作單元(也就是俗稱的任務),而執行機制由Executor來提供。這樣一來Executor是基于生產者消費者模式的,提交任務的操作相當于生成者,執行任務的線程相當于消費者。

1、從類圖上看,Executor接口是異步任務執行框架的基礎,該框架能夠支持多種不同類型的任務執行策略。

public interface Executor {void execute(Runnable command);
}

Executor接口就提供了一個執行方法,任務是Runnbale類型,不支持Callable類型。

2、ExecutorService接口實現了Executor接口,主要提供了關閉線程池和submit方法:

public interface ExecutorService extends Executor {List<Runnable> shutdownNow();boolean isTerminated();<T> Future<T> submit(Callable<T> task);}

另外該接口有兩個重要的實現類:ThreadPoolExecutor與ScheduledThreadPoolExecutor。

其中ThreadPoolExecutor是線程池的核心實現類,用來執行被提交的任務;而ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲后運行任務,或者定期執行命令。

在上一篇文章中,我是使用ThreadPoolExecutor來通過給定不同的參數從而創建自己所需的線程池,但是在后面的工作中不建議這種方式,推薦使用Exectuors工廠方法來創建線程池

這里先來區別線程池和線程組(ThreadGroup與ThreadPoolExecutor)這兩個概念:

a、線程組就表示一個線程的集合。

b、線程池是為線程的生命周期開銷問題和資源不足問題提供解決方案,主要是用來管理線程。

Executors可以創建3種類型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadExecutor和CachedThreadPool

a、SingleThreadExecutor:單線程線程池

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

我們從源碼來看可以知道,單線程線程池的創建也是通過ThreadPoolExecutor,里面的核心線程數和線程數都是1,并且工作隊列使用的是無界隊列。由于是單線程工作,每次只能處理一個任務,所以后面所有的任務都被阻塞在工作隊列中,只能一個個任務執行。

b、FixedThreadExecutor:固定大小線程池

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

這個與單線程類似,只是創建了固定大小的線程數量。

c、CachedThreadPool:無界線程池

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

無界線程池意味著沒有工作隊列,任務進來就執行,線程數量不夠就創建,與前面兩個的區別是:空閑的線程會被回收掉,空閑的時間是60s。這個適用于執行很多短期異步的小程序或者負載較輕的服務器。

Callable、Future、FutureTash詳解

Callable與Future是在JAVA的后續版本中引入進來的,Callable類似于Runnable接口,實現Callable接口的類與實現Runnable的類都是可以被線程執行的任務。

三者之間的關系:

Callable是Runnable封裝的異步運算任務。

Future用來保存Callable異步運算的結果

FutureTask封裝Future的實體類

1、Callable與Runnbale的區別

a、Callable定義的方法是call,而Runnable定義的方法是run。

b、call方法有返回值,而run方法是沒有返回值的。

c、call方法可以拋出異常,而run方法不能拋出異常。

2、Future

Future表示異步計算的結果,提供了以下方法,主要是判斷任務是否完成、中斷任務、獲取任務執行結果

 1 public interface Future<V> {2 3     boolean cancel(boolean mayInterruptIfRunning);4 5     boolean isCancelled();6 7     boolean isDone();8 9     V get() throws InterruptedException, ExecutionException;
10 
11     V get(long timeout, TimeUnit unit)
12         throws InterruptedException, ExecutionException, TimeoutException;
13 }

3、FutureTask

可取消的異步計算,此類提供了對Future的基本實現,僅在計算完成時才能獲取結果,如果計算尚未完成,則阻塞get方法。

public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>

FutureTask不僅實現了Future接口,還實現了Runnable接口,所以不僅可以將FutureTask當成一個任務交給Executor來執行,還可以通過Thread來創建一個線程。

Callable與FutureTask

定義一個callable的任務:

1 public class MyCallableTask implements Callable<Integer>2 {3     @Override4     public Integer call()5         throws Exception6     {7         System.out.println("callable do somothing");8         Thread.sleep(5000);9         return new Random().nextInt(100);
10     }
11 }
public class CallableTest2 {3     public static void main(String[] args) throws Exception4     {5         Callable<Integer> callable = new MyCallableTask();6         FutureTask<Integer> future = new FutureTask<Integer>(callable);7         Thread thread = new Thread(future);8         thread.start();9         Thread.sleep(100);
10         //嘗試取消對此任務的執行
11         future.cancel(true);
12         //判斷是否在任務正常完成前取消
13         System.out.println("future is cancel:" + future.isCancelled());
14         if(!future.isCancelled())
15         {
16             System.out.println("future is cancelled");
17         }
18         //判斷任務是否已完成
19         System.out.println("future is done:" + future.isDone());
20         if(!future.isDone())
21         {
22             System.out.println("future get=" + future.get());
23         }
24         else
25         {
26             //任務已完成
27             System.out.println("task is done");
28         }
29     }
30 }

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/167088.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/167088.shtml
英文地址,請注明出處:http://en.pswp.cn/news/167088.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

這個視頻監控技術,讓你的工作效率提升10倍!

在當今數字時代&#xff0c;視頻監控技術正迅速成為社會安全、商業管理和生產運營的重要支柱。隨著科技的飛速發展&#xff0c;視頻監控不再僅僅是觀察和記錄&#xff0c;而是演變成了一種智能、高效的解決方案。 在這個數字化的時代&#xff0c;視頻監控不僅是一種技術&#x…

Flink-簡介與基礎

Flink-簡介與基礎 一、Flink起源二、Flink數據處理模式1.批處理2.流處理3.Flink流批一體處理 三、Flink架構1.Flink集群2.Flink Program3.JobManager4.TaskManager 四、Flink應用程序五、Flink高級特性1.時間流&#xff08;Time&#xff09;和窗口&#xff08;Window&#xff0…

穿山甲SDK接入收益·android廣告接入·app變現·廣告千展收益·eCPM收益(2023.11)

接入穿山甲SDK的app 全屏文字滾動APP 數獨訓練APP 廣告接入示例: Android 個人開發者如何接入廣告SDK&#xff0c;實現app流量變現 接入穿山甲SDK app示例&#xff1a; android 數獨小游戲 經典數獨休閑益智 2023.11.11 ~ 2023.11.22 app接入上架有一段時間了&#xff0c;接…

移動應用程序管理的內容、原因和方式

移動應用程序管理&#xff08;MAM&#xff09;是一個術語&#xff0c;指的是管理應用程序的整個生命周期&#xff0c;包括從設備安裝、更新和卸載應用程序&#xff0c;除了在整個生命周期內管理設備外&#xff0c;MAM 還包括保護應用訪問的數據&#xff0c;以及在設備上發現惡意…

Oracle 數據庫基線安全加固操作

目錄 賬號管理、認證授權 ELK-Oracle-01-01-01 ELK-Oracle-01-01-02 ???????ELK-Oracle-01-01-03 ???????ELK-Oracle-01-01-04 ???????ELK-Oracle-01-01-05 ???????ELK-Oracle-01-01-06 ??????? ELK-Oracle-01-01-07 ??????? …

Lambda 重構面向對象的設計模式

Lambda 重構面向對象的設計模式 策略模式 策略模式包含三部分內容 一個代表某個算法的接口&#xff08;它是策略模式的接口&#xff09;。 一個或多個該接口的具體實現&#xff0c;它們代表了算法的多種實現&#xff08;比如&#xff0c;實體類ConcreteStrategyA或者Concrete…

java集成poi框架

介紹 : Apache POI是Apache軟件基金會的開放源碼函式庫&#xff0c;POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。 下面簡單介紹一下如何使用該框架&#xff1a; 一&#xff1a;導入依賴 <!-- excel解析依賴--><dependency><groupId&…

17 redis集群方案

1、RedisCluster分布式集群解決方案 為了解決單機內存&#xff0c;并發等瓶頸&#xff0c;可使用此方案解決問題. Redis-cluster是一種服務器Sharding技術&#xff0c;Redis3.0以后版本正式提供支持。 這里的集群是指多主多從&#xff0c;不是一主多從。 2、redis集群的目標…

pair和typedef

文章目錄 一、pair用法1.2、pair的創建和初始化1.3、pair對象的操作1.4、(make_pair)生成新的pair對象1.5、通過tie獲取pair元素值 2、typedef2.1、什么是typedef2.2、typedef用法2.2.1、對于數據類型使用例如&#xff1a;2.2.2、對于指針的使用例如2.2.3、對于結構體的使用 2.…

java springboot測試類虛擬MVC環境 匹配返回值與預期內容是否相同 (JSON數據格式) 版

上文java springboot測試類鑒定虛擬MVC請求 返回內容與預期值是否相同我們講了測試類中 虛擬MVC發送請求 匹配返回內容是否與預期值相同 但是 讓我意外的是 既然沒人罵我 因為我們實際開發 返回的基本都是json數據 字符串的接口場景是少數的 我們在java文件目錄下創建一個 dom…

2023年10月紙巾市場分析(京東天貓淘寶平臺紙巾品類數據采集)

雙十一大促期間&#xff0c;剛需品的紙巾是必囤商品之一。今年雙十一&#xff0c;京東數據顯示&#xff0c;10月23日至29日&#xff0c;清潔紙品成交額同比增長40%&#xff0c;由此也拉動了10月紙巾市場的銷售。 鯨參謀數據顯示&#xff0c;今年10月&#xff0c;京東平臺紙巾市…

【日常總結】如何禁止瀏覽器 http自動跳轉成https

一、場景 二、問題 三、解決方案 3.1 chrome 瀏覽器 3.2 edge 瀏覽器&#xff1a; 3.3 Safari 瀏覽器 3.4 Firefox 瀏覽器 3.5 Microsoft Edge 一、場景 公司網站 http:// 谷歌瀏覽器中自動轉換成 https:// 導致無法訪問 二、問題 nginx配置ssl 443接口&#xff0c; ht…

SOLIDWORKS 2024新功能之Electrical篇

SOLIDWORKS 2024 Electrical篇目錄概覽 ? 對齊零部件 ? 更改多個導軌和線槽的長度 ? 過濾輔助和附件零件 ? 2D 機柜中的自動零件序號 ? 移除制造商零件數據 ? 重置未定義的宏變量 ? 使用范圍縮短列表 ? SOLIDWORKS Electrical Schematic 增強功能 1、對齊零部件…

ONNX實踐系列-修改yolov5-seg的proto分支輸出shape

一、目標 本文主要介紹要將原始yolov5分割的輸出掩膜從[b,c,h,.w]修改為[b, h, w, c] 原來的: 目標的: 代碼如下: Descripttion: version: @Company: WT-XM Author: yang jinyi Date: 2023-09-08 11:26:28 LastEditors: yang jinyi LastEditTime: 2023-09-08 11:48:01 …

Threejs_14 制作圣誕賀卡

繼續跟著老陳打碼學習&#xff01;&#xff01;&#xff01;支持&#xff01;&#xff01;&#xff01; 效果圖 鏈接&#xff1a;https://pan.baidu.com/s/1Ft8U2HTeqmpyAeesL31iUg 提取碼&#xff1a;6666 使用到的 模型文件和資源等都為老陳打碼提供&#xff01;&#x…

【騰訊云云上實驗室】探索保護數據之盾背后的安全監控機制

當今數字化時代&#xff0c;數據安全成為了企業和個人最為關注的重要議題之一。隨著數據規模的不斷增長和數據應用的廣泛普及&#xff0c;如何保護數據的安全性和隱私性成為了迫切的需求。 今天&#xff0c;我將帶領大家一起探索騰訊云云上實驗室所推出的向量數據庫&#xff0c…

新版PY系列離線燒錄器,支持PY002A/002B/003/030/071等MCU各封裝,不同 FLASH 大小型號

PY系列離線燒錄器&#xff0c;目前支持PY32F002A/002B/002/003/030/071/072/040/403/303 各封裝、不同 FLASH 大小型號。PY離線燒錄器需要搭配上位機軟件使用&#xff0c;上位機軟件可以在芯嶺技術官網上下載&#xff0c;還包括了離線燒錄器的使用說明。PY離線燒錄器使用MINI U…

金融機構如何高效率考勤?這個技巧幫了大忙!

在現代社會&#xff0c;隨著科技的不斷發展&#xff0c;人臉識別技術作為一種高效、便捷的身份驗證手段&#xff0c;逐漸應用于各個領域&#xff0c;其中之一便是人臉考勤系統。 傳統的考勤方式存在一系列問題&#xff0c;如卡片打卡容易被冒用、簽到表容易造假等&#xff0c;而…

CTFUB-web前置技能-HTTP協議

burp抓包,抓第二次的 修改請求方式為CTFHUB

算法筆記:OPTICS 聚類

1 基本介紹 OPTICS(Ordering points to identify the clustering structure)是一基于密度的聚類算法 OPTICS算法是DBSCAN的改進版本 在DBCSAN算法中需要輸入兩個參數&#xff1a; ? 和 MinPts &#xff0c;選擇不同的參數會導致最終聚類的結果千差萬別&#xff0c;因此DBCSAN…