Java 併發編程解析 | 關於線程機制的那些事,你究竟瞭解多少?

来源:https://www.cnblogs.com/mazhilin/archive/2022/07/30/16534198.html
-Advertisement-
Play Games

蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》 寫在開頭 眾所周知,在電腦操作系統中,進程(Process)是一個很關鍵的概念,最本質的理解就是操作系統執行的一個應用程式(Application Program)。與每個進程相關的是地址空間(Address ...


蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》

寫在開頭

眾所周知,在電腦操作系統中,進程(Process)是一個很關鍵的概念,最本質的理解就是操作系統執行的一個應用程式(Application Program)。與每個進程相關的是地址空間(Address Space)。其中,描述的是從某個最小值的存儲位置(通常是0)到最大值的存儲位置的列表。在這個地址空間中,進程可以進行讀寫操作。地址空間中可以存放可執行程式,以及程式需要的數據和棧針。與每個進程相關的資源集合。通常包括寄存器(Registers),打開的文件清單,突發的系統報警,有關的進程清單和其他執行程式的信息。其中,寄存器主要包括程式計數器(Program Counter)和堆棧指針(Stack Pointer)。從一定程度上,我們可以把進程當作容納運行一個程式所有信息的一個容器(Container)。

操作系統中可以使用進程來描述一個程式的執行過程,進程擁有該程式的所有數據(包括一些I/O分配情況、記憶體分配情況等),也就是該程式的一個載體,所以進程有一個特點就是資源分配的單位,這一點十分重要。進程還有一個特點就是調度執行,交替執行以提高資源利用率。

操作系統管理進程(創建、切換進程、分配與回收等)開銷是很大的,比如進程創建時還需要創建PCB,分配記憶體獨立的內空間,建立映射表,創建資源,進程切換時還需要切換資源,如切換對應的記憶體映射表,進程退出時還需要釋放資源。

由此不難得出一個結論,每一個進程都有一個地址空間(Address Space)和一個控制線程(Control Thead)。但是,操作系統有了進程為何要出現線程呢?主要是因為,雖然進程任然是資源分配的單位,但是調度執行卻交給了線程,因為線程是在進程的內部,線程間的切換不用切換資源,不用切換映射表,只需要簡單的在進程內部切換一下PC指針和保存一些寄存器即可,這也就更輕量了(避免不了不同進程間的線程切換)。

基本概述

線程(Thread)既保留了併發執行的優點,也避免了進程切換的代價。

假設現在有一個網路伺服器,此時沒有線程的概念,該伺服器程式用到多個進程,如用一個進程監聽客戶端的請求,當客戶端連接上後就分派出(複製出一個子進程)一個進程給該用戶(每個進程都有獨立的資源),用於監聽該用戶發送的數據並處理(即多進程程式),此時設想一下,這多個進程切來切去,每次切換的時候都需要切換資源,是不是很耗費資源。

此時,引入了線程之後,網路伺服器這個程式是一個進程,進程用於承載該程式的資源,首先用進程中的一個線程監聽請求,每次連接客戶端都分配一個線程給用戶(多線程程式),此時處理器只需要在這幾個線程中切換即可,線程的切換不需要切換資源(進程時資源級的切換,線程是指令級的切換),那麼多個線程只需要共用進程中的資源即可,其運行速度和執行效率也得到了提升。

由此可見,操作系統引入線程後,調度和分派是線上程上完成的,但是某些活動會影響進程中的所有線程,因此這些活動必須在進程級對他們進行管理。如掛起操作會掛起所有線程,因為所有線程共用進程的用戶地址空間。引入了線程最關鍵的體現在以下兩個方面:

  • 線程的創建、終止和調度更輕量
  • 線程間的通信不進過內核,不需要用戶態->內核態的轉換

但是,同時也增加了程式的開發難度,如果開發者對於線程機制的掌握和認識不夠準確,也會陷入技術困惑。

線程模型

所有線程共用進程的狀態和資源,所以線程都駐留在同一塊地址空間中,並可訪問相同的數據。

對於有線程和無線程的區別,其中主要是體現在用戶棧和內核棧兩個關鍵:

  • 用戶棧用於保存用戶進程的子程式間相互調用的參數、返回值以及局部變數等信息(保存普通方法的棧)
  • 內核棧是程式發生系統調用時內核態調用方法時的棧;用戶地址空間則是進程的程式和數據存放的空間,線程是沒有自己的用戶地址空間的

一般來說,用戶棧和內核棧已經線上程中獨有,也證明瞭線程成為了任務調度的基本單位,這些線程都共用進程所持有的資源,線程式控制制塊中存放了寄存器的值、優先順序、線程狀態等信息。

在操作系統層面,線程也有“生老病死”,專業的說法叫有生命周期。雖然不同的開發語言對於操作系統線程進行了不同的封裝,但是對於線程的生命周期這部分,基本上是相通的。每一個線程基本都有如下特征:

  1. 類似進程,線程也有執行狀態(生命周期),因為線程也是一個執行過程
  2. 線程的上下文,線程切換時也需要進行保護現場
  3. 執行棧,保存系統調用時的一些參數和中間結果
  4. 少量的,線程私有的局部變數的存儲空間,不再擁有大量的存儲空間
  5. 與進程內其他線程共用的記憶體和資源的訪問
  6. 線程式控制制塊TCB,存放上下文切換的信息,同PCB

可以看出,對於有生命周期的事物,要學好和掌握它,思路非常簡單,只要能搞懂生命周期中各個節點的狀態轉換機制即可。

線程分類

線程分為用戶級線程(User-Level Thread,ULT)和內核級線程(Kernel-Level Thread,KLT),內核級線程又叫做輕量級進程(Light-Weight Process,LWP)。

用戶級線程(User-Level Thread,ULT)

在純ULT軟體中,管理線程的所有工作都是應用程式完成,內核意識不到線程的存在,線程完全是由線程庫提供的,創建、銷毀、調度線程、線程間傳遞消息等,還包括保存上下文都是由它管控的,如果可以的話我們自己也可以實現自己的線程庫,只要合理的組織線程即可。

但是用戶級線程所有的活動都發生在用戶空間和一個進程中,系統感知不到用戶級線程的存在,所以系統依舊是以進程的方式調度。

當線程1發生系統調用等阻塞了,此時系統就會認為該進程阻塞了,操作系統會把CPU時間片分配給其他進程,在此期間,根據線程庫維護的數據結構來看,線程1任然處於運行狀態,但在處理器執行的角度,線程2是不處於運行狀態的,也分不到時間片。

這也導致了用戶級線程一旦阻塞,就會阻塞進程中的所有線程,使得其他線程也得不到運行。使用用戶級線程(ULT)如下特點:

優點:

  • 所有線程的管理都在一個進程的用戶空間中,線程的切換不需要內核模式特權,不需要系統調用,從而節省了用戶態到內核態轉換的開銷
  • 線程的調度更靈活,可以為每個不同的應用程式量身定製更合適的調度演算法,因為這些調度演算法都可以自己實現,不需要更改操作系統底層的調度程式
  • ULT可以在任何操作系統下運行,即便是不支持線程的操作系統也能實現,線程庫是供所有應用程式共用的一組應用級函數

缺點:

  • 在執行一個系統調用時不僅僅是阻塞當前線程,還會阻塞進程中的所有線程
  • ULT不能利用多處理技術,操作系統看不到線程,所以內核一次只能把一個進程分配給一個處理器,因此一個進程中的所有線程不能夠並行執行,只能夠併發執行,相當於一個進程內實現了多道程式設計

解決這兩個問題的方法有:

  • 把應用程式寫出多進程程式,但是該方法消除了線程的主要優點
  • 套管技術:把產生阻塞的系統調用轉化為一個非阻塞的系統調用

綜上所述,用戶級線程(User-Level Thread,ULT)適合計算密集型的,因為不需要IO操作 ,不會阻塞整個進程。

內核級線程(Kernel-Level Thread,KLT)

在KLT軟體中,管理線程的所有工作均由內核完成,應用級沒有線程管理代碼,只有一個到內核線程的API。

內核為進程級進程內的所有線程維護上下文信息,調度由內核基於線程完成。

該方法剋服了ULT的兩個缺點。首先,內核可以把一個進程中的線程分配個多個處理器中;其次,進程中的某個線程阻塞了,內核還可以調度同一個進程中的其他線程。

缺點是:在把控制權從一個線程傳送到另一個進程的線程時,需要切換到內核模式,開銷較大。

綜上所述,KLT併發性更好,適合I/O操作較多的程式。

混合線程(Hybrid-Approach Thread,HAT)

有些操作系統提供了ULT和KLT的混合體:線程創建完全在用戶空間中完成,線程的調度和同步也在應用程式中進行,一個應用程式中的多個用戶級線程會被映射到一些(小於等於用戶級線數)內核級線程上,進程和線程的比為 N:M,N<=M,ULT中比值為1:N,KLT為1:1

同一個應用程式中的多個線程可在多個處理器上並行的運行,某個引起阻塞的系統調用不會阻塞整個進程。

綜上所述,內核級線程(KLT)和用戶級線程(User-Level Thread,ULT)對比分析如下:

線程生命周期

一個線程的生命周期基本上可以這個“五態模型”來描述,主要分別是:初始狀態、可運行狀態、運行狀態、休眠狀態和終止狀態。其中:

  1. 初始狀態,指的是線程已經被創建,但是還不允許分配 CPU 執行。這個狀態屬於編程語言特有的,不過這裡所謂的被創建,僅僅是在編程語言層面被創建,而在操作系統層面,真正的線程還沒有創建。
  2. 可運行狀態,指的是線程可以分配 CPU 執行。在這種狀態下,真正的操作系統線程已經被成功創建了,所以可以分配 CPU 執行。
  3. 運行狀態:當有空閑的 CPU 時,操作系統會將其分配給一個處於可運行狀態的線程,被分配到 CPU 的線程的狀態就轉換成了運行狀態。
  4. 休眠狀態:運行狀態的線程如果調用一個阻塞的 API(例如以阻塞方式讀文件)或者等待某個事件(例如條件變數),那麼線程的狀態就會轉換到休眠狀態,同時釋放 CPU 使用權,休眠狀態的線程永遠沒有機會獲得 CPU 使用權。當等待的事件出現了,線程就會從休眠狀態轉換到可運行狀態。
  5. 終止狀態:線程執行完或者出現異常就會進入終止狀態,終止狀態的線程不會切換到其他任何狀態,進入終止狀態也就意味著線程的生命周期結束。

版權聲明:本文為博主原創文章,遵循相關版權協議,如若轉載或者分享請附上原文出處鏈接和鏈接來源。


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

-Advertisement-
Play Games
更多相關文章
  • 配置 Webpack Dev Server 可以解決本地開發前端應用時,手動執行 webpack 命令或 yarn build 命令,再去瀏覽器中訪問 dist/index.html 的麻煩耗時操作,可有效簡化流程。本文實戰演練配置 Webpack Dev Server 的方法與步驟。 ...
  • FrameDataTrans教程 博客園 乳鴿菌 20220729 核心原理是使用postMessage發送數據,window.addEventListener("message",fun)監聽。 插件地址 index.htm // <iframe id="fr" src="child.html"> ...
  • 記錄一個清除計時器的小bug,使用計時器的時候迴圈一直停不下來,把計時器賦值給了變數,仍舊無法停止。後來發現是清除器放置的位置有問題。 最初的位置: 這樣放置就導致if語句值運行了一次!我潛意識預設了整個getprogress方法為迴圈體,但其實只有那個計時器部分是迴圈體,導致我百思不得其解為什麼循 ...
  • 是不是見到google,facebook等大型專業網站的擁有不同的語言站群,可以不同語言間切換很給力 今天要介紹的就是如何識別不同國家語言,只需要簡單幾步,使你的web應用更有國際範。 安裝vue-i18n npm i vue-i18n --save 新建多語言json文件 在src目錄下新建 co ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 最近鼓搗了一下 Vue3 + Vite2,遇到了不少問題,整理了5個可以提高開發效率的小知識,讓你在 Vue3 的項目開發中更加絲滑、順暢。 一、setup name 增強 Vue3的setup語法糖是個好東西,但使用setup語法 ...
  • 最近寫二維碼的時候,突然想起之前項目遇到過的一個問題,網上也沒有這方面解答,想到大家今後可能也會遇到這類問題,在此記錄下來,希望對你們有所幫助,大佬們不喜勿噴,qrcode配合畫布canvas本地生成二維碼的時候第一次能夠正常顯示,最下方會貼出代碼 跨頁面後顯示異常 剛開始是有些頭疼的,因為控制台一 ...
  • 自定義事件 tips 推薦始終使用 kebab-case 的事件名。(v-on會將事件名自動轉換為小寫,避免匹配不到) changeData × change-data √ 自定義組件的v-model 用法: 父組件定義數據源(不需要定義修改數據的方法),在子組件標簽上通過v-model="data ...
  • 因為涉及到有些監控是沒有音頻的,所以使用flv.js插件,通過<web-view>標簽跳轉到h5頁面。 在真機上調試發現是沒有發現flv.js文件,出現跨域現象,但是我只是導入了js文件,不知道什麼情況; 然後,就只是移動端不支持flv.js。經過不懈努力,我找到了另外一個插件 DPlayer.js ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...