Tomcat線程池原理(下篇:工作原理)

文章目錄

  • 前言
  • 正文
    • 一、執行線程的基本流程
      • 1.1 JUC中的線程池執行線程
      • 1.2 Tomcat 中線程池執行線程
    • 二、被改造的阻塞隊列
      • 2.1 TaskQueue的 offer(...)
      • 2.2 TaskQueue的 force(...)
    • 三、總結

前言

Tomcat 線程池,是依據 JUC 中的線程池 ThreadPoolExecutor 重新自定義實現的。
其執行線程的代碼邏輯,和JUC 中是相同的。主要區別在于,Tomcat中對 阻塞隊列進行了改造。

本文主要研究 Tomcat 的線程池是如何執行線程的,即線程池的工作原理。

同系列文章:Tomcat線程池原理(上篇:初始化原理)

正文

一、執行線程的基本流程

Tomcat 中執行線程的基本流程,和JUC中是一致的。
以下貼出兩種執行方法。

1.1 JUC中的線程池執行線程

ThreadPoolExecutor中,執行線程的方法定義如下:

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);
}

1.2 Tomcat 中線程池執行線程

@Override
public void execute(Runnable command) {execute(command,0,TimeUnit.MILLISECONDS);
}

重載方法定義如下:
這個方法被Deprecated標注,源碼中注釋中描述說是,Tomcat10中會被刪除,估計是一種優化。本文不做研究。

@Deprecated
public void execute(Runnable command, long timeout, TimeUnit unit) {// 提交的任務數量+1submittedCount.incrementAndGet();try {// 執行線程任務(這個方法中的代碼和JUC中執行線程的代碼一致,差別在于,阻塞隊列使用了Tomcat自定義的隊列)executeInternal(command);} catch (RejectedExecutionException rx) {// 被拒絕后,嘗試將線程放入隊列// 如果當前隊列是Tomcat自定義的隊列TaskQueue,嘗試將任務放入隊列if (getQueue() instanceof TaskQueue) {final TaskQueue queue = (TaskQueue) getQueue();try {// 強制將線程任務放入阻塞隊列if (!queue.force(command, timeout, unit)) {// 放入隊列失敗,提交的任務數-1submittedCount.decrementAndGet();// 拋出異常,線程池隊列已滿throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));}} catch (InterruptedException x) {// 中斷時,提交的任務數-1submittedCount.decrementAndGet();// 拋出異常throw new RejectedExecutionException(x);}} else {// 當前指定的隊列不是TaskQueue,提交的任務數-1,并拋出異常submittedCount.decrementAndGet();throw rx;}}
}

另外,executeInternal 的內容如下(和JUC中基本一致):區別在于阻塞隊列換成了TaskQueue

private void executeInternal(Runnable command) {if (command == null) {throw new NullPointerException();}int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true)) {return;}c = ctl.get();}// 這里的workQueue,實際已經換成了TaskQueueif (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);}
}

二、被改造的阻塞隊列

從第一小節中,看到的東西并不多。因為,Tomcat線程池的改造重心,在于阻塞隊列,也就是 TaskQueue

2.1 TaskQueue的 offer(…)

@Override
public boolean offer(Runnable o) {//we can't do any checksif (parent==null) {return super.offer(o);}// 若是達到最大線程數,直接進隊列if (parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()) {return super.offer(o);}// 已提交任務數小于等于當前線程數// 對應的場景是:當前活躍線程為10個,但是只有8個任務在執行,于是,直接進隊列if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) {return super.offer(o);}// 當前線程數小于最大線程數,此次拒絕進入隊列if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {return false;}//if we reached here, we need to add it to the queuereturn super.offer(o);
}

2.2 TaskQueue的 force(…)

其本質是,直接入隊列。

public boolean force(Runnable o) {if (parent == null || parent.isShutdown()) {throw new RejectedExecutionException(sm.getString("taskQueue.notRunning"));}return super.offer(o); //forces the item onto the queue, to be used if the task is rejected
}

三、總結

根據源碼中對于TaskQueue的改造,可以觀察出,Tomcat線程池的主要特點如下:

  • 當前線程數小于corePoolSize,則去創建工作線程;
  • 當前線程數大于corePoolSize,但小于maximumPoolSize,則去創建工作線程;
  • 當前線程數大于maximumPoolSize,則將任務放入到阻塞隊列中,當阻塞隊列滿了之后,則調用拒絕策略丟棄任務;
  • 任務執行失敗時不會直接拋出錯誤,而是裝回隊列里再次嘗試執行;
  • 當線程池沒有達到最大執行線程的時候,會優先開線程再使用任務隊列;

總結之后就是,Tomcat 為了更適配 IO 密集型任務,改造了阻塞隊列。與JUC相比,會先去創建線程執行任務,創建的線程數達到最大線程數時,再放入隊列等待空閑線程的出現。

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

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

相關文章

深入理解C語言(5):程序環境和預處理詳解

文章主題&#xff1a;程序環境和預處理詳解&#x1f30f;所屬專欄&#xff1a;深入理解C語言&#x1f4d4;作者簡介&#xff1a;更新有關深入理解C語言知識的博主一枚&#xff0c;記錄分享自己對C語言的深入解讀。&#x1f606;個人主頁&#xff1a;[?]的個人主頁&#x1f3c4…

Imagewheel私人圖床搭建結合內網穿透實現無公網IP遠程訪問教程

文章目錄 1.前言2. Imagewheel網站搭建2.1. Imagewheel下載和安裝2.2. Imagewheel網頁測試2.3.cpolar的安裝和注冊 3.本地網頁發布3.1.Cpolar臨時數據隧道3.2.Cpolar穩定隧道&#xff08;云端設置&#xff09;3.3.Cpolar穩定隧道&#xff08;本地設置&#xff09; 4.公網訪問測…

flutter 文件上傳組件和大文件分片上傳

文件分片上傳 資料 https://www.cnblogs.com/caijinglong/p/11558389.html 使用分段上傳來上傳和復制對象 - Amazon Simple Storage Service 因為公司使用的是亞馬遜的s3桶 下面是查閱資料獲得的 亞馬遜s3桶的文件上傳分片 分段上分為三個步驟&#xff1a;開始上傳、上傳對…

CSP-J 2023 T3 一元二次方程

文章目錄 題目題目背景題目描述輸入格式輸出格式樣例 #1樣例輸入 #1樣例輸出 #1 提示 題目傳送門題解思路總代碼 提交結果尾聲 題目 題目背景 眾所周知&#xff0c;對一元二次方程 a x 2 b x c 0 , ( a ≠ 0 ) ax ^ 2 bx c 0, (a \neq 0) ax2bxc0,(a0)&#xff0c;可…

STM32G030C8T6:定時器1ms中斷(以64MHz外部晶振為例)

本專欄記錄STM32開發各個功能的詳細過程&#xff0c;方便自己后續查看&#xff0c;當然也供正在入門STM32單片機的兄弟們參考&#xff1b; 本小節的目標是&#xff0c;系統主頻64 MHZ,采用高速外部晶振&#xff0c;通過定時器3 每秒中斷控制 PB9 引腳輸出高低電平&#xff0c;從…

20240222作業

完善對話框&#xff0c;點擊登錄對話框&#xff0c;如果賬號和密碼匹配&#xff0c;則彈出信息對話框&#xff0c;給出提示“登錄成功"&#xff0c;提供一個Ok按鈕&#xff0c;用戶點擊OK后&#xff0c;關閉登錄界面&#xff0c;跳轉到其他界面 如果賬號和密碼不匹配&…

Java基礎-注解

注解 注解是用來干什么的它有什么作用注解的常見分類內置注解Override注解定義 Deprecated注解定義 SuppressWarnings注解定義 元注解Target注解定義ElementType Retention&&RetentionTarget注解定義RetentionPolicy Documented注解定義 Inherited注解定義用法 Repeata…

低代碼開發:推動互聯網企業數字化轉型的關鍵因素

聯網行業作為我國數字經濟發展的核心驅動力&#xff0c;在推動國家數字化轉型中扮演著至關重要的角色。與其他傳統行業相比&#xff0c;互聯網企業面臨更加緊迫的數字化轉型需求&#xff0c;因為它們需要不斷適應快速變化的市場環境和技術趨勢。 然而&#xff0c;由于互聯網企業…

深入理解APDU協議與Java開發

1. 什么是APDU&#xff1f; APDU&#xff0c;即應用協議數據單元&#xff08;Application Protocol Data Unit&#xff09;&#xff0c;是一種在智能卡與卡片讀卡器之間進行通信的協議。APDU定義了在交互中傳輸的數據格式和規則&#xff0c;允許讀卡器發送指令并接收響應。 2…

MFC 皮膚庫配置

1.創建MFC 對話框 2.添加皮膚資源 添加資源 添加頭文件 關閉SDL檢測 添加靜態庫文件 修改字符集 添加頭文件 將皮膚中的ssk文件加載到初始化實例中 > 運行即可

springboot 的 websocket 里面使用 @Autowired 注入 service 或 bean 時,報空指針異常

直接上解決方案&#xff1a; 在你的WebSocketServer服務器中 public static MessageService messageService; //要注入的類// 注入的時候&#xff0c;給類的 service 注入Autowiredpublic void setChatService(MessageService messageService) {WebSocketServer.messageSer…

【寸鐵的刷題筆記】樹、dfs、bfs、回溯、遞歸(一)

【寸鐵的刷題筆記】樹、dfs、bfs、回溯、遞歸(一) 大家好 我是寸鐵&#x1f44a; 總結了一篇刷題關于樹、dfs、bfs、回溯、遞歸的文章? 喜歡的小伙伴可以點點關注 &#x1f49d; 105. 從前序與中序遍歷序列構造二叉樹 模擬分析圖 代碼實現 /*** Definition for a binary tre…

HarmonyOS—添加/刪除Module

Module是應用/服務的基本功能單元&#xff0c;包含了源代碼、資源文件、第三方庫及應用/服務配置文件&#xff0c;每一個Module都可以獨立進行編譯和運行。一個HarmonyOS應用/服務通常會包含一個或多個Module&#xff0c;因此&#xff0c;可以在工程中創建多個Module&#xff0…

如何利用內網穿透工具在企業微信開發者中心實現本地接口服務回調

文章目錄 1. Windows安裝Cpolar2. 創建Cpolar域名3. 創建企業微信應用4. 定義回調本地接口5. 回調和可信域名接口校驗6. 設置固定Cpolar域名7. 使用固定域名校驗 企業微信開發者在應用的開發測試階段&#xff0c;應用服務通常是部署在開發環境&#xff0c;在有數據回調的開發場…

SQL查詢每個類別價格前3的數據

SELECTproduct_id,category,price FROM (SELECTproduct_id,category,price,ROW_NUMBER() OVER (PARTITION BY category ORDER BY price) AS rankFROMyour_products_table ) AS ranked_products WHERErank < 3;DENSE_RANK() 和 ROW_NUMBER() 是窗口函數&#xff08;Window Fu…

前端知識復習

1.symbol類型 Symbol 是 ECMAScript 6 中引入的一種新的基本數據類型&#xff0c;它表示獨一無二的值。Symbol 值是通過 Symbol() 函數創建的。 Symbol 值具有以下特點&#xff1a; 獨一無二性&#xff08;唯一性&#xff09;&#xff1a;每個通過 Symbol() 函數創建的 Symb…

十三:集合

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 01、Java 集合框架概述1.1、集合框架與數組的對比及概述1.2、集合框架涉及到的API 02、Collection接口方法2.1、Collection接口中的常用方法12.2、Collection接口中…

在idea中配置Tomcat

1.在idea中點擊右上角 2.點擊Edit Configurations,點擊加號 3.向下拉找到Tomcat Server下的Local,點一下 點擊Configure 找到tomcat文件路徑,選擇apache-tomcat-8.5.63(8.5.63是我的版本號) 選擇好路徑后點ok就配置好了 總步驟:

Vue 圖片輪播第三方庫 Vue-awesome-swiper介紹及簡單例子

vue-awesome-swiper 是一個基于 Swiper 的 Vue 輪播圖組件&#xff0c;Swiper 是一個流行的移動端觸摸滑動插件。它為 Vue.js 應用提供了一套豐富的輪播組件&#xff0c;支持多種布局和功能&#xff0c;如自動播放、無限循環、觸摸滑動等。 安裝 首先&#xff…

代碼隨想錄算法訓練營第一天

● 今日學習的文章鏈接和視頻鏈接 ● 自己看到題目的第一想法 1. 704二分法&#xff1a; 方法一&#xff1a; 整個數組是 左閉右閉區間 [ ] left指針指向數組開始下標&#xff0c; right 指針指向數組最后下表nums.size()-1, mid為 (leftright) /2循環條件 left<rightnu…