支持JDK19虛擬線程的web框架,之五(終篇):興風作浪的ThreadLocal

来源:https://www.cnblogs.com/bolingcavalry/archive/2022/11/14/16887918.html
-Advertisement-
Play Games

ThreadLocal,這個多線程場景中重要的特性,在虛擬線程領域興風作浪,為了應付它,quarkus也是夠拼,今天咱們就來聊聊這個話題,在輕鬆的氣氛中結束《支持JDK19虛擬線程的web框架》系列 ...


歡迎訪問我的GitHub

這裡分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

《支持JDK19虛擬線程的web框架》系列文章鏈接

本篇概覽

  • 本篇是《支持JDK19虛擬線程的web框架》系列的第五篇,也是全系列的終篇,之前的文章實戰、寫代碼、讀源碼,想必把大家累壞了,今天咱們開啟聊天模式,暢談虛擬線程中的一個關鍵問題,在輕鬆的氣氛中學習知識,也為整個系列順利收官

關於ThreadLocal

  • 既然提到了線程,自然繞不開ThreadLocal類,它提供了線程本地變數,此變數和一般的變數不同。通過get & set 方法,每個線程可以獲取到自己獨立的變數。這個變數實例通常是私有且靜態的,可以存儲與線程相關的信息,如產品id、事務id等。

  • 下圖很形象的展現了ThreadLocal:是完全屬於每個線程自己的集合

multithreading - How is Java's ThreadLocal implemented under the hood? -  Stack Overflow

虛擬線程中,ThreadLocal的問題

  • 既然每個線程都可以擁有屬於自己的ThreadLocal對象,那虛擬線程的情況又如何呢?
  • 虛擬線程的特性,使得我們可以在應用代碼中創建成千上萬個虛擬線程去執行併發任務,而無需擔心線程數量對整體計算資源的負擔,如果每個線程都用了ThreadLocal,那會不會出現成千上萬的ThreadLocal對象呢?線程是虛擬的,對象可是實實在在的,這樣會增加系統資源消耗,或者影響性能嗎?
  • 不過轉念一想,這麼明顯的問題,連我都能想到,JDK組織又豈會漏掉?應該是我多慮了吧,憑自己"豐富的經驗",我預測解決方案應該和TLAB(Thread Local Allocation Buffer)類似,為海量虛擬線程的ThreadLoacal對象建立映射關係,做到高效管理
  • 然而現實很殘酷,臉,被狠狠地抽打,通過Oracle官方博客,知道實際情況真慘...,如下圖,中文註釋是我的解讀,極具悲觀色彩,如果翻譯得不准確請您告知,謝謝

image-20221023195706881

  • 對上述內容,個人理解是以下兩點:
  1. 虛擬線程中使用ThreadLocal確實會帶來記憶體問題,現在還無解,連虛擬線程自身的工程Loom都在自己代碼中刪除ThreadLocal的使用,那麼我們普通用戶敢用嗎?還是避而遠之吧,在虛擬線程中不要用ThreadLocal
  2. 編號429的JEP,為我們帶來了一個解決方案,一種名為Scoped values的變數,可以在一定範圍(scope)內被訪問,至於這個scope,可以是一個記憶體範圍(例如臨時變數就只能在方法內),另外還有一種範圍被稱為dynamic scope,這個範圍就更加靈活了,不過這個JEP當前的狀態還很早期,如下圖,還在提案階段,這要是跳票了或者被否了,那我博客不就白寫了?就此打住吧,我不能再研究了
image-20221023202006694
  • 搞清楚以上問題後,自己的八卦之心就控制不住了:既然虛擬線程上的ThreadLocal問題這麼嚴重,放眼Java世界的生態這麼繁榮,那麼多框架和庫,那麼...你們說
  1. 有沒有哪個倒霉蛋掉進這個坑裡去?

  2. 慘不慘?

  3. 從坑裡爬出來沒有?

  • 你別說,還真有...
image-20221023203456166

踩坑勇士quarkus

  • 這位踩坑勇士,就是貫穿整個《支持JDK19虛擬線程的web框架》系列的quarkus,來吧,一起圍觀quarkus踩坑,順便學點知識
  • 先看quarkus官方文檔《virtual-threads.adoc》,如下圖

image-20221023205121235

  • 我對上述內容的理解:
  1. quarkus的人發現:傳統線程池模式改用虛擬線程後,性能提升明顯,但是反應式框架改用虛擬線程後的提升並不明顯,而且還會帶來記憶體消耗過大的問題(看過前面ThreadLocal分析的您,此刻應該猜到原因了了,嘿嘿,您猜的沒錯)
  2. 如果您的應用對記憶體有較嚴要求,quarkus官方建議您繼續堅持(stick)使用反應式框架(這話中透露出濃濃的無可奈何,別催了,搞不定...)
  • 接下來官方就要甩鍋了,有趣的是,這次接鍋的並非JDK,而是大名鼎鼎的...Netty

Netty的問題

  • 為什麼是Netty接鍋呢?
  • 首先,Netty使用了Reactor線程模型,而Netty Reactor的核心是Event Loop,下圖來自《Netty in Action》,是處理web請求的內部架構圖,

img

  • 那麼,應該有多少個EventLoop線程呢?下圖是Netty源碼,預設值是CPU核數的2倍,看得出這是個很保守的數字

image-20221023211649460

  • 從上面的架構圖和代碼可以看出,Netty的反應式框架的核心是使用少量線程來分發web請求,這樣的結果僅使用了少量線程資源就能高效處理事件
  • 也正式因為有了線程數不多這個前提,在對JSON做序列化處理時,Netty放心的使用了ThreadLocal,畢竟線程少,一個4核的CPU也才8個ThreadLocal,毫無壓力
  • 而且,為了更加高效,Netty還對ThreadLoacal進行過改造,也就是他們自研的FastThreadLocal
  • 然後,時間一天天過去,終於等來了JDK19發佈,
  • quarkus的反應式web服務模塊底層就是Netty,為了用上虛擬線程,他們動手了...咱們腦補一下吧,鋪天蓋地的虛擬線程線程,鋪天蓋地的FastThreadLocal對象,炸了吧您...Are U OK ?
  • 快樂之後,咱們還是要正視這個問題,錶面上看是個坑,實際上是兩種設計思路的衝突:
  1. 虛擬線程的特性類似golang的協程,很適合直接拿來處理高併發web請求,為每個請求分配一個虛擬線程,邏輯清晰直白,資源消耗又不高,典型的簡單高效
  2. Netty的反應式模型,核心思路就是用少量線程高效分發大量請求,本身就很高效,而且就算優化,線程數也不是瓶頸
  3. 所以,quarkus拎著虛擬線程衝到Netty的地盤一陣操作猛如虎,一看結果...唉,扯遠了,來看quarkus官方的解釋吧

image-20221023220236112

  • 上圖紅框中那句話很有價值,咱們都能從中領悟到一些東西,我的收穫是:當線程數不是系統瓶頸的時候,就別衝動,強行上虛擬線程沒用

quarkus強行輓尊

  • 既然虛擬線程不適合反應式模型,個人認為:那就不妨大大方方的承認Netty的Reactor是優秀的,放棄將虛擬線程加入進來,這樣不是挺好麽?
  • 然而quarkus接下來的操作還是把我嚇到了:既然虛擬線程不適合反應式模型?那就想辦法強行讓它適合,下圖就是quarkus的做法:在構建階段,找到創建ThreadLocal的那段代碼,修改它的位元組碼,以此來解決前面的記憶體問題

image-20221023221345980

  • 然後我就翻到了上圖提到的那段代碼

image-20221023222844204

  • 好奇心驅使,我點開上圖那個NettyCurrentAdaptor去看了下源碼,當時就一陣頭暈眼花,ASM風格的代碼您能撐多久?試試下圖

image-20221023223201021

  • 按照官方的說法,經過他們的優化有百分之八十的提升,終於快要達到之前反應式框架的水平了
  • 呃,搞得這麼辛苦,也只是快要追上而已,那行,咱不用了行嗎?
  • 另外,上面說的優化手段也不是預設開啟的,還要做以下幾步操作
  1. maven的pom.xml添加以下依賴
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-netty-loom-adaptor</artifactId>
</dependency>
  1. 編譯構建的時候,增加參數-Dnet.bytebuddy.experimental

  2. 啟動的時候,增加參數--add-opens java.base/java.lang=ALL-UNNAMED

  • 上述操作算,quarkus的手段,我這個草根只能仰望,能開拓自己的見識:原來還可以這樣解決問題
  • 但我自己是絕對不敢模仿的,開玩笑,在編輯階段註入代碼,難度太大,並且後面如何維護和交接?

小結

  • 至此,咱們壓測做了,代碼寫了,源碼讀了,八卦也看了,《支持JDK19虛擬線程的web框架》系列也到了和您說再見的時候
  • 虛擬線程很誘人,欣宸和您一樣,迫不及待的想在實際項目中將其用上,實實在在的解決一些問題,正是有了這個目標,才促進了《支持JDK19虛擬線程的web框架》系列的誕生,本著為我所用的心態去學習、瞭解、模仿、鑽研,希望在虛擬線程發佈的早期階段,該系列文章能豐富您的知識面,為您的決策提供參考信息,助您在掌握新技術的時候順利搶占先機
  • 系列雖然結束了,欣宸原創不會停止,這裡永遠是咱們Java愛好者的寧靜港灣,歡迎您的關註

歡迎關註博客園:程式員欣宸

學習路上,你不孤單,欣宸原創一路相伴...


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 迷人的兩度搜索 1、BFS和DFS 深度優先搜索演算法(DFS)和廣度優先搜索演算法(BFS)是一種用於遍歷或搜索樹或圖的演算法,在搜索遍歷的過程中保證每個節點(頂點)訪問一次且僅訪問一次,按照節點(頂點)訪問順序的不同分為深度優先和廣度優先。 1.1、深度優先搜索演算法 深度優先搜索演算法(Depth-Fi ...
  • 今天跟大家分享一個關於“狀態機”的話題。給你講清楚什麼是狀態機、為什麼需要狀態機、適用場景、有哪些具體的實現方案以及各個方案對比(附帶github源碼地址) ...
  • 現代應用已經進入多數據源階段了,不再是一個單一的資料庫包打天下,一個應用中會涉及除關係資料庫外各種數據源,如文本文件類數據、NOSQL、多維資料庫、HTML Webservice等等,即使是關係資料庫,也可能不止一個 應用這樣了,那麼應用中的報表自然也會涉及到多樣性的數據源了 現在的報表,基本都是用 ...
  • 命題邏輯里的一個法則 定義:非p或非q=非(p且q) 最近在看一本書啊《python工匠......》一個騰訊大佬寫的,從這裡面瞭解到這個東西,確實不錯 1 1 # 德摩根定律 2 2 def func(): 3 3 a = 10 4 4 b = 20 5 5 if not a < 5 or not ...
  • 出品 | OSC開源社區(ID:oschina2013) Meta 發佈了一篇博客表示,正在將其 Android 應用的 Java 代碼遷移到 Kotlin,並分享了這一過程中的一些經驗。 該公司認為,Kotlin 是一種流行的 Android 開發語言,與 Java 相比具有一些關鍵優勢。“因此, ...
  • ReentrantLock和Synchronized都是Java開發中最常用的鎖,與Synchronized這種JVM內置鎖不同的是,ReentrantLock提供了更豐富的語義。可以創建公平鎖或非公平鎖、響應中斷、超時等待、按條件喚醒等。在某些場景下,使用ReentrantLock更適合,功能更強... ...
  • 第一章-緒論 一、數據結構的三要素 1、邏輯結構 數據結構著重關註的是數據元素之間的關係,和對這些數據元素的操作,而不關心具體的數據項內容 集合結構 定義:各個元素同屬於一個集合,別無其他關係; 線性結構(一對一)——>第二、三章 定義: 除了第一個元素,所有元素都有唯一前驅; 除了最後一個元素,所 ...
  • JZ65不用加減乘除做加法 描述 寫一個函數,求兩個整數之和,要求在函數體內不得使用+、-、*、/四則運算符號。 數據範圍:兩個數都滿足 -10 \le n \le 1000−10≤n≤1000 進階:空間複雜度 O(1)O(1),時間複雜度 O(1)O(1) 方法一:位運算非遞歸(推薦使用) 思路 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...