主頁 > 軟體設計 > Java 并發編程決議 | 如何正確理解Java領域中的多執行緒模型,主要用來解決什么問題?

Java 并發編程決議 | 如何正確理解Java領域中的多執行緒模型,主要用來解決什么問題?

2022-08-13 08:54:01 軟體設計

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

Navigation-Picture

寫在開頭

Header-Picture

我國宋代禪宗大師青原行思在《三重境界》中有這樣一句話:“ 參禪之初,看山是山,看水是水;禪有悟時,看山不是山,看水不是水;禪中徹悟,看山仍然山,看水仍然是水,”

作為一名Java Developer,在面對Java并發編程的時候,有過哪些的疑惑與不解 ?對于Java領域中的執行緒機制與多執行緒,你都做了哪些功課?是否和我一樣,在看完《Java編程思想》和《Java并發編程實戰》之后,依舊一頭霧水,不知其跡?那么,希望你看完此篇文章之后,對你有所幫助,

從一定程度上說,Java并發編程之路,實則是一條“看山是山,看山不是山,看山還是山”的修行之路,大多數情況下,當我們覺得有跡可循到有跡可尋時,何嘗不是陷入了另外一個“怪圈”之中?

從搭載Linux系統上的服務器程式來說,使用Java撰寫的是”單行程-多執行緒"程式,而用C++語言撰寫的,可能是“單行程-多執行緒”程式,“多行程-單執行緒”程式或者是“多行程-多執行緒”程式,其中,“多行程-多執行緒”程式是”單行程-多執行緒"程式和“多行程-單執行緒”程式的組合體,

相對于作業系統內核來說,Java程式屬于應用程式,只能在這一個行程里面,一般我們都是直接利用JDK提供的API開發多個執行緒實作并發,

而C++直接運行在Linux系統上,可以直接利用Linux系統提供的強大的行程間通信(Inter-Process Communication,IPC),很容易創建多個行程實作并發程式,并實作行程間通信,

但是,多執行緒的開發難度遠遠高于單執行緒的開發,主要是需要處理執行緒間的通信,需要對執行緒并發做控制,需要做好執行緒間的協調作業,

對于固定負載情況下,在描述和研究計算并發系統處理能力,以及描述并行處理效果的加速比,一直有一個比較著名的計算公式:

vJgupF.png

就是我們熟知的阿姆達爾定律(Amdahl"s Law),在這個公式中,

[1]. P:指的是程式中可并行部分的程式在單核上執行的時間占比,一般用作表示可改進性能的部件原先運行占用的時間與系統整體運行需要的時間的比值,取值范圍是0 ≤ P ≤ 1,

[2]. S:指的是處理器的個數(總核心數),一般用作表示升級加速比,可改進部件原先運行速度與改進后的部件速度的比值,取值范圍是S ≥ 1,

[3]. Slatency(s):指的是程式在S個處理器相對在單個處理器(單核)中速度提升比率,一般用作表示整個任務的提速比,

根據這個公式,我們可以依據可確定程式中可并行代碼的比例,來決定我們實際作業中增加處理器(總核心數)所能帶來的速度提升的上限,

無論是C++開發者在Linux系統中使用的pthread,還是Java開發者使用的java.util.concurrent(JUC)庫,這些執行緒機制的都需要一定的執行緒I/O模型來做理論支撐,

所以,接下來,我們就讓我們一起探討和揭開Java領域中的執行緒I/O模型的神秘面紗,針對那些盤根錯落的枝末細節,才能讓我們更好地了解和正確認識ava領域中的執行緒機制,

關健術語

vGZMCR.png

本文用到的一些關鍵詞語以及常用術語,主要如下:

  • 阿姆達爾定律(Amdahl 定律): 用于確定并發系統中性能瓶頸部件在采用措施提示性能后,此部件對系統性能提示的改行程度,即系統加速比,
  • 任務(Task): 表示一個程式需要被完成作業內容,與執行緒非一對一對應的關系,是一個相對概念,
  • 并發(Concurrent): 表示至少一個任務或者若干 個任務同一個時間段內被執行,但是不是順序執行,大多數都是以交替的方式被執行,
  • 并行(Parallel): 表示至少一個任務或者若干 個任務同一個時刻被執行,主要是指一個并行連接通過多個通道在同一時間內傳播多個資料流,
  • 串行(Serial): 表示至多一個任務或者只有一個 個任務同一個時刻被執行,主要是指在同一時間內只連接傳輸一個資料流,
  • 內核執行緒(Kernel Thread): 表示由內核管理的執行緒,處于作業系統內核空間,用戶應用程式通過API和系統呼叫(system call)來訪問執行緒工具,
  • 應用執行緒(Application Thread): 表示不需要內核支持而在用戶應用程式中實作的執行緒,處于應用程式空間,也稱作用戶執行緒,主要是由JVM管理的執行緒和JVM自己攜帶的JVM執行緒,
  • 背景關系切換(Context Switch): 一般是指任務切換, 或者CPU暫存器切換,當多任務內核決定運行另外的任務時, 它保存正在運行任務的當前狀態, 也就是CPU暫存器中的全部內容,這些內容被保存在任務自己的堆疊中, 入堆疊作業完成后就把下一個將要運行的任務的當前狀況從該任務的堆疊中重新裝入CPU暫存器, 并開始下一個任務的運行程序,在Java領域中,執行緒有生命周期,其背景關系資訊的保存和恢復的程序,
  • 執行緒安全(Thread Safe): 一段操作共享資料的代碼能夠保證同一個時間內被多個執行緒執行而依然保證其資料的正確性的考量,

基本概述

Java領域中的執行緒主要分為Java層執行緒(Java Thread) ,JVM層執行緒(JVM Thread),作業系統層執行緒(Kernel Thread),

Content-Picture

對于Java領域中,從一定程度上來說,由于Java程式并不直接運行在Linux系統上,而是運行在JVM(Java 虛擬機)上,而一個JVM實體是一個Linux行程,每一個JVM都是一個獨立的“沙盒”,JVM之間相互獨立,互不通信,

按照作業系統和應用程式兩個層次來說,執行緒主要可以分為內核執行緒(Kernel Thread) 和應用執行緒(Application Thread),

其中,在Java領域中的執行緒主要分為Java層執行緒(Java Thread) ,JVM層執行緒(JVM Thread),作業系統層執行緒(Kernel Thread),

一般來說,我們把應用執行緒看作更高層面的執行緒,而內核執行緒需要向應用執行緒提供支持,由此可見,內核執行緒和應用執行緒之間存在一定的映射關系,

因此,從執行緒映射關系來看,不同的作業系統可能采用不同的映射方式,我們把這些映射關系稱為執行緒的映射,或者可以說作執行緒映射理論模型(Thread Mappered Theory Model ),

在Java領域中,對于檔案的I/O操作,提供了一系列的I/O功能API,主要基于基于流模型實作,我們把這些流模型的設計,稱作為I/O流模型(I/O Stream Model ),

其中,Java對照作業系統內核以及網路通信I/O中的傳統BIO來說,提供并支持了NIO和AIO的功能API設計,我們把這些設計,稱作為執行緒I/O參考模型(Thread I/O Reference Model ),

另外,對于NIO和AIO還參考了一定的設計模式來實作,我們把這些基于設計模式的設計,稱作為執行緒設計模式模型(Thread I/O Design Pattern Model ),

綜上所述,在Java領域中,我們在學習和掌握Java并發編程的時候,可以按照:執行緒映射理論模型->I/O流模型->執行緒I/O參考模型->執行緒設計模式模型->執行緒價值模型等脈絡來一一進行對比分析,

一. Java 領域中的執行緒映射理論模型

Java 領域中的執行緒映射模型主要有內核級執行緒模型(Kernel-Level Thread ,KLT)、應用級執行緒模型(Application-Level Thread ,ALT)、混合兩級執行緒模型(Mixture-Level Thread ,MLT)等3種模型,

Mapperd-Picture

從Java執行緒映射型別來看,主要有執行緒一對一(1:1)映射,執行緒多對多(M:1)映射,執行緒多對多(M:N)映射等關系,

對應到執行緒模型來說,執行緒一對一(1:1)映射對應著內核執行緒(Kernel-Level Thread ,KLT),執行緒多對多(M:1)映射對應著應用級執行緒(Application-Level Thread,ALT),執行緒多對多(M:N)映射對應著混合兩級執行緒(Mixture-Level Thread ,MLT),

因此,Java領域中實作多執行緒主要有3種模型:內核級執行緒模型、應用級執行緒模型、混合兩級執行緒模型,它們之間最大的差異就在于執行緒與內核調度物體( Kernel Scheduling Entity,簡稱KSE)之間的對應關系上,

顧名思義,內核調度物體就是可以被內核的調度器調度的物件,因此稱為內核級執行緒,是作業系統內核的最小調度單元,

綜上所述,接下來,我們來詳細討論Java 領域中的執行緒映射理論模型,

1. 應用級執行緒模型

應用級執行緒模型主要是指(Application-Level Thread ,ALT),就是多個用戶執行緒映射到同一個內核執行緒上,用戶執行緒的創建、調度、同步的所有操作全部都是由用戶空間的執行緒來完成的,

vGAEuD.png

在Java領域中,應用級執行緒主要是指Java語言撰寫應用程式的Java 執行緒(Java Thread)和JVM虛擬機中JVM執行緒(JVM Thread),

在應用級執行緒模型下,完全建立在用戶空間的執行緒庫上,不依賴于系統內核,用戶執行緒的創建、同步、切換和銷毀等操作完全在用戶態執行,不需要切換到內核態,

其中,用戶行程使用系統內核提供的介面——輕量級行程(Light Weight Process,LWP)來使用系統內核執行緒,

在此種執行緒模型下,由于一個用戶執行緒對應一個LWP,因此某個LWP在呼叫程序中阻塞了不會影響整個行程的執行,

但是各種執行緒的操作都需要在用戶態和內核態之間頻繁切換,消耗太大,速度相對用戶執行緒模型來說要慢,

2. 內核級執行緒模型

內核級執行緒模型主要是指(Kernel-Level Thread ,KLT),用戶執行緒與內核執行緒建立了一對一的關系,即一個用戶執行緒對應一個內核執行緒,內核負責每個執行緒的調度,

vGAJbQ.png

在Linux中,對于內核級執行緒,作業系統會為其創建一套堆疊:用戶堆疊+內核堆疊,其中用戶堆疊作業在用戶態,內核堆疊作業在內核態,在發生系統呼叫時,執行緒的執行會從用戶堆疊切換到內核堆疊,

在內核級執行緒模型下,完全依賴作業系統內核提供的內核執行緒來實作多執行緒,執行緒的切換調度由系統內核完成,系統內核負責將多個執行緒執行的任務映射到各個CPU中去執行,

其中,glibc中的pthread_create方法主要是創建一個OS內核級執行緒,我們不深入細節,主要是為該執行緒分配了堆疊資源;需要注意的是這個堆疊資源對于JVM而言是堆外記憶體,因此堆外記憶體的大小會影響JVM可以創建的執行緒數,

在JVM概念中,JVM堆疊用來執行Java方法,而本地方法堆疊用來執行native方法;但需要注意的是JVM只是在概念上區分了這兩種堆疊,而并沒有規定如何實作,

在HotSpot中,則是將JVM堆疊與本地方法堆疊二合一,使用核心執行緒的用戶堆疊來實作(因為JVM堆疊和本地方法堆疊都是屬于用戶態的堆疊),即Java方法與native方法都在同一個用戶堆疊中呼叫,而當發生系統呼叫時,再切換到核心堆疊運行,

這種設計的好處是執行緒的各種操作以及切換消耗很低;

但是執行緒的所有操作都需要在用戶態實作,執行緒的調度實作起來例外復雜,并且系統內核對ULT無感知,如果執行緒阻塞則會引起整個行程的阻塞,

3. 混合兩級執行緒模型

混合兩級執行緒模型主要是指(Mixture-Level Thread ,MLT),是應用級執行緒模型和內核級執行緒模型等兩種模型的混合版本,用戶執行緒仍然是在用戶態中創建,用戶執行緒的創建、切換和銷毀的消耗很低,用戶執行緒的數量不受限制,

MLT-Picture

對于混合兩級執行緒模型,是應用級執行緒模型和內核級執行緒模型等兩種模型的混合版本,主要是充分吸收前面兩種執行緒模型的優點且盡量規避它們的缺點,

在此模型下用戶執行緒與內核執行緒是多對多(M : N,通常M >= N)的映射模型,主要是維護一個輕量級行程(Light Weight Process,LWP),在用戶執行緒和內核執行緒之間充當橋梁,就可以使用作業系統提供的執行緒調度和處理器映射功能,

一般來說,Java虛擬機使用的執行緒模型是基于作業系統提供的原生執行緒模型來實作的,Windows系統和Linux系統都是使用的內核執行緒模型,而Solaris系統支持混合執行緒模型和內核執行緒模型兩種實作,

還有,Java執行緒記憶體模型中,可以將虛擬機記憶體劃分為兩部分記憶體:主記憶體和執行緒作業記憶體,主記憶體是多個執行緒共享的記憶體,執行緒作業記憶體是每個執行緒獨享的記憶體,方法區和堆記憶體就是主記憶體區域,而虛擬機堆疊、本地方法堆疊以及程式計數器則屬于每個執行緒獨享的作業記憶體,

Java記憶體模型規定所有成員變數都需要存盤在主記憶體中,執行緒會在其作業記憶體中保存需要使用的成員變數的拷貝,執行緒對成員變數的操作(讀取和賦值等)都是對其作業記憶體中的拷貝進行操作,各個執行緒之間不能互相訪問作業記憶體,執行緒間變數的傳遞需要通過主記憶體來完成,

二. Java 領域中的I/O流模型

Java 領域中的I/O模型主要指Java 領域中的I/O模型大致可以分為字符流I/O模型,位元組流I/O模型以及網路通信I/O模型,

vGFReJ.png

在編程語言的I/O類別庫中常使用流(Stream)這個概念,代表了任何有能力產出資料的資料源物件或者是有能力接收資料的接收端物件,

流是個抽象的概念,是對輸入輸出設備的高度抽象,一般來說,編程語言都會涉及輸入流和輸出流兩部分,

一定意義上來說,輸入流可以看作一個輸入通道,輸出流可以看作一個輸出通道,其中:

  • 輸入流是相對程式而言的,外部傳入資料給程式需要借助輸入流,
  • 輸出流是相對程式而言的,程式把資料傳輸到外部需要借助輸出流,

由于,“流”模型屏蔽了實際的I/O設備中處理資料的細節,這就意味著我們只需要根據相關的基礎API的功能和設計,便可實作資料處理和互動,

Java IO 方式有很多種,基于不同的 IO 抽象模型和互動方式,可以進行簡單區分:
第一,傳統的 java.io 包,它基于流模型實作,提供了我們最熟知的一些 IO 功能,比如 File 抽象、輸入輸出流等,互動方式是同步、阻塞的方式,也就是說,在讀取輸入流或者寫入輸出流時,在讀、寫動作完成之前,執行緒會一直阻塞在那里,它們之間的呼叫是可靠的線性順序,java.io 包的好處是代碼比較簡單、直觀,缺點則是 IO 效率和擴展性存在局限性,容易成為應用性能的瓶頸,

很多時候,人們也把 java.net 下面提供的部分網路 API,比如 Socket、ServerSocket、HttpURLConnection 也歸類到同步阻塞 IO 類別庫,因為網路通信同樣是 IO 行為,

第二,在 Java 1.4 中引入了 NIO 框架(java.nio 包),提供了 Channel、Selector、Buffer 等新的抽象,可以構建多路復用的、同步非阻塞 IO 程式,同時提供了更接近作業系統底層的高性能資料操作方式,

第三,在 Java 7 中,NIO 有了進一步的改進,也就是 NIO 2,引入了異步非阻塞 IO 方式,也有很多人叫它 AIO(Asynchronous IO),異步 IO 操作基于事件和回呼機制,可以簡單理解為,應用操作直接回傳,而不會阻塞在那里,當后臺處理完成,作業系統會通知相應執行緒進行后續作業,

其中,Java類別庫中的I/O類分成輸入和輸出兩部分,主要是對應著實作我們與計算機操作互動時的一種規范和約束,但是對于不同的資料有著不同的實作,

綜上所述,Java 領域中的I/O模型大致可以分為字符流I/O模型,位元組流I/O模型以及網路通信I/O模型等3類,

1. 位元組流I/O模型

位元組流I/O模型是指在I/O操作,資料傳輸程序中,傳輸資料的最基本單位是位元組的流,按照8位傳輸位元組為單位輸入/輸出資料,

vGAcVJ.png

在Java 領域中,對位元組流的類通常以stream結尾,對于位元組資料的操作,提供了輸入流(InputStream)、輸出流(OutputStream)這樣式的設計,是用于讀取或寫入位元組的基礎API,一般常用于操作類似文本或者圖片檔案,

2. 字符流I/O模型

字符流I/O模型是指在I/O操作,資料傳輸程序中,傳輸資料的最基本單位是字符的流,按照16位傳輸字符為單位輸入/輸出資料,

vGAf8x.png

在Java 領域中,對字符流的類通常以reader和writer結尾,對于位元組資料的操作,提供了輸入流(Reader)、輸出流(Writer)這樣式的設計,是用于讀取或寫入位元組的基礎API,一般常用于類似從檔案中讀取或者寫入文本資訊,

3. 網路通信I/O模型

網路通信I/O模型是指java.net 下,提供的部分網路 API,比如 Socket、ServerSocket、HttpURLConnection 等IO 類別庫,實作網路通信同樣是 IO 行為,

vGAqIA.png

在Java領域中,NIO提供了與傳統BIO模型中的Socket和ServerSocket相對應的SocketChannel和ServerSocketChannel兩種不同的套接字通道實作,SocketChannel可以看作是 socket 的一個完善類,除了提供 Socket 的相關功能外,還提供了許多其他特性,如后面要講到的向選擇器注冊的功能,

其中,新增的SocketChannel和ServerSocketChannel兩種通道都支持阻塞和非阻塞兩種模式,

三. Java 領域中的執行緒I/O參考模型

在Java領域中,我們對照執行緒概念(單執行緒和多執行緒)來說,可以分為Java 執行緒-阻塞I/O模型和Java 執行緒-非阻塞I/O模型兩種,

vGiTbj.png

由于阻塞與非阻塞主要是針對于應用程式對于系統函式呼叫角度來限定的,從阻塞與非阻塞的意義上來說,I/O可以分為阻塞I/O和非阻塞I/O兩種大類,其中:

  • 阻 塞 I/O : 進行I/O操作時,使當前執行緒進入阻塞狀態,從具體應用程式來看,如果當一次I/O操作(Read/Write)沒有就緒或者沒有完成,則函式呼叫則會一直處于等待狀態,
  • 非阻塞I/O:進行I/O操作時,使當前執行緒不進入阻塞狀態,從具體應用程式來看,如果當一次I/O操作(Read/Write)即使沒有就緒或者沒有完成,則函式呼叫立即回傳結果,然后由應用程式輪詢處理,

而同步與異步主要正針對應用程式對于系統函式呼叫后,其I/O操作中讀/寫(Read/Write)是由誰完成來限定的,I/O可以分為同步I/O和異步I/O兩種大類,其中:

  • 同步I/O: 進行I/O操作時,可以使當前執行緒進入進入阻塞或或非阻塞狀態,從具體應用程式來看,如果當一次I/O操作(Read/Write)都是托管給應用程式來完成,
  • 異步I/O: 進行I/O操作時,可以使當前執行緒進入進入非阻塞狀態,從具體應用程式來看,如果當一次I/O操作(Read/Write)都是托管給作業系統來完成,完成后回呼或者事件通知應用程式,

由此可見,按照這些個定義可以知道:

  • 當程式在執行I/O操作時,經典的網路I/O操作(Read/Write)場景,主要可以分為阻塞I/O,非阻塞I/O,單執行緒以及多執行緒等場景,
  • 異步I/O一定是非阻塞I/O,不存在是異步還阻塞的情況;同步I/O可能存在阻塞或或非阻塞的情況,還有可能是I/O執行緒多路復用的情況,

因此,我們可以對其執行緒I/O模型來說,I/O可以分為同步-阻塞I/O和同步-非阻塞I/O,以及異步I/O等3種,其中I/O多路復用屬于同步-阻塞I/O,

綜上所所述,,在Java領域中,我們對照執行緒概念(單執行緒和多執行緒)來說,可以分為Java 執行緒-阻塞I/O模型和Java 執行緒-非阻塞I/O模型兩種,接下來,我們就詳細地來探討一下,

(一). Java 執行緒阻塞I/O模型

Java 執行緒-阻塞I/O模型主要可以分為單執行緒阻塞I/O模型和多執行緒阻塞I/O模型,

vGFbOe.png

從一個服務器處理客戶端連接來說,單執行緒情況下,一般都是以一個執行緒負責處理所有客戶端連接的I/O操作(Read/Write)操作,

程式在執行I/O操作,一般都是從內核空間復制資料,但內核空間的資料可能需要很長的時間去準備資料,由此很有可能導致用戶空間產生阻塞,

其產生阻塞的程序,主要如下:

  • 應用程式發起I/O操作(Read/Write)之后,進入阻塞狀態,然后提交給作業系統內核完成I/O操作,
  • 當內核沒有準備資料,需要不斷從網路中讀取資料,一旦準備就緒,則將資料復制到用戶空間供應用程式使用,
  • 應用程式從發起讀取資料操作到繼續執行后續處理的這段時間,便是我們說的阻塞狀態,

由此可見,引入Java執行緒的概念,我們可以把Java 執行緒-阻塞I/O模型主要可以分為單執行緒阻塞I/O模型和多執行緒阻塞I/O模型,

1. 單執行緒阻塞I/O模型

單執行緒阻塞I/O模型主要是指對于多個客戶端訪問時,只能同時處理一個客戶端的訪問,并且在I/O操作上是阻塞的,執行緒會一直處于等待狀態,直到當前執行緒中前一個客戶端訪問結束后,才繼續開始下一個客戶端的訪問,

v8QD5F.png

單執行緒阻塞I/O模型是最簡單的服務器模型,是Java Developer面對網路編程最基礎的模型,

由于對于多個客戶端訪問時,只能同時處理一個客戶端的訪問,并且在I/O操作上是阻塞的,執行緒會一直處于等待狀態,直到當前執行緒中前一個客戶端訪問結束后,才繼續開始下一個客戶端的訪問,

也就意味著,客戶端的訪問請求需要一個一個排隊等待,只提供一問一答的服務機制,

這種模型的特點,主要在于單執行緒和阻塞I/O,其中:

  • 單執行緒 :指的是服務器端只有一個執行緒處理客戶端的請求,客戶端連接與服務器端的處理執行緒比例關系為N:1,無法同時處理多個連接,只能串行方式連接處理,
  • 阻塞I/O:服務器在I/O操作(Read/Write)操作時是阻塞的,主要表現在讀取客戶端資料時,需要等待客戶端發送資料并且把作業系統內核中的資料復制到用戶空間中的用戶執行緒中,完成后才解除阻塞狀態;同時,資料回寫客戶端要等待用戶行程把資料寫入到作業系統系統內核后才解除阻塞狀態,

綜上所述,單執行緒阻塞I/O模型最明顯的特點就是服務機制簡單,服務器的系統資源開銷小,但是并發能力低,容錯能力也低,

2. 多執行緒阻塞I/O模型

多執行緒阻塞I/O模型主要是指對于多個客戶端訪問時,利用多執行緒機制為每一個客戶端的訪問分配獨立執行緒,實作同時處理,并且在I/O操作上是阻塞的,執行緒不會一直處于等待狀態,而是并發處理客戶端的請求訪問,

v8QcvR.png

多執行緒阻塞I/O模型是針對于單執行緒阻塞I/O模型的缺點,對其進行多執行緒化改進,使之能對于多個客戶端的請求訪問實作并發回應處理,

也就意味著,客戶端的訪問請求不需要一個一個排隊等待,利用多執行緒機制為每一個客戶端的訪問分配獨立執行緒,

這種模型的特點,主要在于多執行緒和阻塞I/O,其中:

  • 多執行緒 :指的是服務器端至少有一個執行緒或者若干個執行緒處理客戶端的請求,客戶端連接與服務器端的處理執行緒比例關系為M:N,并發同時處理多個連接,可以并行方式連接處理,但客戶端連接與服務器處理執行緒的關系是一對一的,
  • 阻塞I/O:服務器在I/O操作(Read/Write)操作時是阻塞的,主要表現在讀取客戶端資料時,需要等待客戶端發送資料并且把作業系統內核中的資料復制到用戶空間中的用戶執行緒中,完成后才解除阻塞狀態;同時,資料回寫客戶端要等待用戶行程把資料寫入到作業系統系統內核后才解除阻塞狀態,

綜上所述,多執行緒阻塞I/O模型最明顯的特點就是支持多個客戶端并發回應,處理能力得到極大提高,有一定的并發能力和容錯能力,但是服務器資源消耗較大,且多執行緒之間會產生執行緒切換成本,結構也比較復雜,

(二). Java 執行緒非阻塞I/O模型

Java 執行緒-非阻塞I/O模型主要可以分為應用層I/O多路復用模型和內核層I/O多路復用模型,以及內核回呼事件驅動I/O模型,

vGkVkn.png

從一個服務器處理客戶端連接來說,多執行緒情況下,一般都是至少一個執行緒或者若干個執行緒負責處理所有客戶端連接的I/O操作(Read/Write)操作,

非阻塞I/O模型與阻塞I/O模型,相同的地方在于是程式在執行I/O操作,一般都是從內核空間和應用空間復制資料,

與之不同的是,非阻塞I/O模型不會一直等到內核空間準備好資料,而是立即回傳去做其他的事,因此不會產生阻塞,其中:

  • 應用程式中的用戶執行緒包含一個緩沖區,單個執行緒會不斷輪詢客戶端,以及不斷嘗試進行I/O(Read/Write)操作,

  • 一旦內核準好資料,應用程式中的用戶執行緒就會把資料復制到用戶空間使用,

由此可見,我們可以把Java 執行緒-非阻塞I/O模型主要可以分為應用層I/O多路復用模型和內核層I/O多路復用模型,以及內核回呼事件驅動I/O模型,

1. 應用層I/O多路復用模型

應用層I/O多路復用模型主要是指當多個客戶端向服務器發出請求時,服務器會將每一個客戶端連接維護到一個socket串列中,應用程式中的用戶執行緒會不斷輪詢sockst串列中的客戶端連接請求訪問,并嘗試進行讀寫,

v8lCxs.png

應用層I/O多路復用模型最大的特點就是,不論有多少個socket連接,都可以使用應用程式中的用戶執行緒的一個執行緒來管理,

這個執行緒負責輪詢socket串列,不斷進行嘗試進行I/O(Read/Write)操作,其中:

  • I/O(Read)操作:如果成功讀取資料,則對資料進行處理,反之,如果失敗,則下一個回圈再繼續嘗試,
  • I/O(Write)操作:需要先嘗試把資料寫入指定的socket,直到呼叫成功結束,反之,如果失敗,則下一個回圈再繼續嘗試,

這種模型,雖然很好地利用了阻塞的時間,使得批處理能提升,但是由于不斷輪詢sockst串列,同時也需要處理資料的拼接,

2. 內核層I/O多路復用模型

內核層I/O多路復用模型主要是指當多個客戶端向服務器發出請求時,服務器會將每一個客戶端連接維護到一個socket串列中,作業系統內核不斷輪詢sockst串列,并把遍歷結果組織羅列成一系列的事件,并驅動事件回傳到應用層處理,最后托管給應用程式中的用戶執行緒按照需要處理對應的事件物件,

v8lCxs.png

內核層I/O多路復用模型與應用層I/O多路復用模型,最大的不同就是,輪詢sockst串列是作業系統內核來完成的,有助于檢測效率,

作業系統內核負責輪詢socket串列的程序,其中:

  • 首先,最主要的就是將所有連接的標記為可讀事件和可寫事件串列,最后傳入到應用程式的用戶空間處理,
  • 然后,作業系統內核復制資料到應用層的用戶空間的用戶執行緒,會隨著socket數量的增加,也會形成不小的開銷,
  • 另外,當活躍連接數比較少時,內核空間和用戶空間會存在很多無效的資料副本,并且不管是否活躍,都會復制到用戶空間的應用層,

3. 內核回呼事件驅動I/O模型

內核回呼事件驅動I/O模型主要是指當多個客戶端向服務器發出請求時,服務器會將每一個客戶端連接維護到一個socket串列中,作業系統內核不斷輪詢sockst串列,利用回呼函式來檢測socket串列是否可讀可寫的一種事件驅動I/O機制,

v8lKz9.png

不論是內核層的輪詢sockst串列,還是應用層的輪詢sockst串列,通過回圈遍歷的方式來檢測socket串列是否可讀可寫的操作方式,其效率都比較低效,

為了尋求一種高效的機制來優化回圈遍歷方式,因此,提出了會回呼函式事件驅動機制,其中,主要是:

  • 內核空間:當客戶端往socket發送資料時,內核中socket都對應著一個回呼函式,內核就可以直接從網卡中接收資料后,直接呼叫回呼函式,
  • 應用空間:回呼函式會維護一個事件串列,應用層則獲取事件即可以得到感興趣的事件,然后進行后續操作,

一般來說,內核回呼事件驅動的方式主要有2種:

  • 第一種:利用可讀串列(ReadList)和可寫串列(WriteList)來標記讀事件(Read-Event)/寫事件(Write-Event)來進行I/O(Read/Write)操作,
  • 第二種:利用在應用層中直接指定socket感興趣的事件,通過維護事件串列(EventList)再來進行I/O(Read/Write)操作,

綜上所述,這兩種方式都是有作業系統內核維護客戶端中的所有連接,再通過回呼函式不斷更新事件串列,應用空間中的應用層的用戶執行緒只需要根據輪詢遍歷事件串列即可知道是否進行I/O(Read/Write)操作,

由此可見,這種方式極大地提高了檢測效率,也增強了資料處理能力,

特別指出,在Java領域中,非阻塞I/O的實作完全是基于作業系統內核的非阻塞I/O,Java把作業系統中的非阻塞I/O的差異最大限度的屏蔽并提供了統一的API,JDK自己會幫助我們選擇非阻塞I/O的實作方式,

一般來說,在Linux系統中,只要支持epoll,JDK會優先選擇epoll來實作Java的非阻塞I/O,

(三). Java 執行緒異步I/O模型

Java 執行緒異步I/O模型主要是指異步非阻塞模型(AIO模型), 需要作業系統負責將資料讀寫到應用傳遞進來的緩沖區供應用程式操作,

v8TSDe.png

對于非阻塞I/O模型(NIO)來說,異步I/O模型的作業機制來說,與之不同的是采用“訂閱(Subscribe)-通知(Notification)”模式,主要如下:

vYZivt.png

  • 訂閱(Subscribe): 用戶執行緒通過作業系統呼叫,向內核注冊某個IO操作后,即應用程式向作業系統注冊IO監聽,然后繼續做自己的事情,
  • 通知(Notification):當作業系統發生IO事件,并且準備好資料后,即內核在整個IO操作(包括資料準備、資料復制)完成后,再主動通知應用程式,觸發相應的函式,執行后續的業務操作,

在異步IO模型中,整個內核的資料處理程序中,包括內核將資料從網路物理設備(網卡)讀取到內核快取區、將內核緩沖區的資料復制到用戶緩沖區,用戶程式都不需要阻塞,

由此可見,異步I/O模型(AIO模型)需要依賴作業系統的支持,CPU資源開銷比較大,最大的特性是異步能力,對socket和I/O起作用,適合連接數目比較多以及連接時間長的系統架構,

一般來說,在作業系統里,異步IO是指Windows系統的IOCP(Input/Output Completion Port),或者C++的網路庫asio,

在Linux系統中,aio雖然是異步IO模型的具體實作,但是由于不成熟,現在大部分還是依據是否支持epoll等,來模擬和封裝epoll實作的,

在Java領域中,支持異步I/O模型(AIO模型)是Jdk 1.7版本開始的,基于CompletionHandler介面來實作操作完成回呼,其中分別有三個新的異步通道,AsynchronousFileChannel,AsynchronousSocketChannel和AsynchronousServerSocketChannel,

但是,對于支持異步編程模式是在Jdk 1.5版本就已經存在,最典型的就是基于Future模型實作的Executor和FutureTask,

由于Future模型存在一定的局限性,在JDK 1.8 之后,對Future的擴展和增強實作又新增了一個CompletableFuture,

由此可見,在Java領域中,對于異步I/O模型提供了異步檔案通道(AsynchronousFileChannel)和異步套接字通道(AsynchronousSocketChannel和AsynchronousServerSocketChannel)的實作, 其中:

  • 首先,對于異步檔案通道的實作,提供兩種方式獲取操作結果:
    • 通過java.util.concurrent.Future類來表示異步操作的結果:
    • 在執行異步操作的時候傳入一個java.nio.channels.CompletionHandler介面的實作類作為操作完成的回呼,
  • 其次,對于異步套接字通道的是實作:
    • 異步Socket Channel是被動執行物件,不需要像 NIO編程那樣創建一個獨立的 I/O執行緒來處理讀寫操作,
    • 對于AsynchronousServerSocketChannel和AsynchronousSocketChannel 都由JDK底層的執行緒池負責回呼并驅動讀寫操作,
    • 異步套接字通道是真正的異步非阻塞I/O,它對應UNIX網路編程中的事件驅動I/O (AIO),它不需要通過多路復用器(Selector)對注冊的通道進行輪詢操作即可實作異步讀寫, 從而簡化了 NIO的編程模型,

綜上所述,對于在Java領域中的異步IO模型,我們在使用的時候,需要依據實際業務場景需要而進行選擇和考量,

??[特別注意]:

[1].IOCP: 輸入輸出完成埠(Input/Output Completion Port,IOCP), 是支持多個同時發生的異步I/O操作的應用程式編程介面,

[2].epoll: Linux系統中I/O多路復用實作方式的一種,主要是(select,poll,epoll),都是同步I/O,同時也是阻塞I/O,

[3].Future: 屬于Java JDK 1.5 版本支持的編程異步模型,在包java.util.concurrent.下面,

[4].CompletionHandler: 屬于Java JDK 1.7 版本支持的編程異步I/O模型,在包java.nio.channels.下面,

[5].CompletableFuture: 屬于Java JDK 1.8 版本對Future的擴展和增強實作編程異步I/O模型,在java.util.concurrent.下面,

四. Java 領域中的執行緒設計模型

Java 領域中的執行緒設計模型最典型就是基于Reactor模式設計的非阻塞I/O模型和 基于Proactor 模式設計的異步I/O模型和基于Promise模式的Promise模型,

v806u8.png

在Java領域中,對于并發編程的支持,不僅提供了執行緒機制,也引入了多執行緒機制,還有許多同步和異步的實作,

單從設計原則和實作來說,都采用了許多設計模式,其中多執行緒機制最常見的就是執行緒池模式,

對于非阻塞I/O模型,主要采用基于Reactor模式設計,而異步I/O模型,主要采用基于Proactor 模式設計,

當然,還有基于Promise模式的異步編程模型,不過這算是一個特例,

綜上所述,Java 領域中的執行緒設計模型最典型就是基于Reactor模式設計的非阻塞I/O模型和 基于Proactor 模式設計的異步I/O模型和基于Promise模式的Promise模型,

1. 多執行緒非阻塞I/O模型

多執行緒非阻塞I/O模型是針對于多執行緒機制而設計的,根據CPU的數量來創建執行緒數,并且能夠讓多個執行緒并行執行的非阻塞I/O模型,
v8Q55D.png

現在的計算機大多數都是多核CPU的,而且作業系統都提供了多執行緒機制,但是我們也沒有辦法抹掉單執行緒的優勢,

單執行緒最大的優勢就是一個CPU只負責一個執行緒,對于多執行緒中出現的疑難雜癥,它都可以避免,而且編碼簡單,

在一個執行緒對應一個CPU的情況下,如果多核計算機中 只執行一個執行緒,那么就只有一個CPU作業,無法充分發揮CPU和優勢,且資源也無法充分利用,

因此,我們的程式則可以根據CPU的數量來創建執行緒數,N個CPU對應多個N個執行緒,便可以充分利用多個CPU,同時也保持了單執行緒的特點,相當于多個執行緒并行執行而不是并發執行,

在多核計算機時代,多執行緒和非阻塞都是提升服務器處理性能的利器,一般我們都是將客戶端連接按照分組分配給至少一個執行緒或者若干執行緒,每個執行緒負責處理對應組的連接,

在Java領域中,最常見的多執行緒阻塞I/O模型就是基于Reactor模式的Reactor模型,

vGEMdJ.png

2. 基于Reactor模式的Reactor模型

Reactor模型是指在事件驅動的思想上,基于Reactor的作業模式而設計的非阻塞I/O模型(NIO 模型),一定程度上來說,可以說是主動模式I/O模型,

v8lURH.png

對于Reactor模式,我特意在網上查詢了一下資料,查詢的結果都是無疾而終,解釋更是五花八門的,最后,參考一些資料整理得出結論,

參考一下Doug Lea大師在文章“Scalable IO in Java”中對Reactor模式的定義:

Reactor模式由Reactor執行緒、Handlers處理器兩大角色組成,兩大角色的職責分別如下:

  • Reactor執行緒的職責:負責回應IO事件,并且分發到Handlers處理器,
  • Handlers處理器的職責:非阻塞的執行業務處理邏輯,

個人理解,Reactor模式是指在事件驅動的思想上,通過一個或多個輸入同時傳遞給服務處理器的服務請求的事件驅動處理模式,其中,基本思想有兩個:

  • 基于 I/O 復用模型:多個連接共用一個阻塞物件,應用程式只需要在一個阻塞物件等待,無需阻塞等待所有連接,當某個連接有新的資料可以處理時,作業系統通知應用程式,執行緒從阻塞狀態回傳,開始進行業務處理

  • 基于執行緒池復用執行緒資源:不必再為每個連接創建執行緒,將連接完成后的業務處理任務分配給執行緒進行處理,一個執行緒可以處理多個連接的業務,

總體來說,Reactor模式有點類似事件驅動模式,在事件驅動模式中,當有事件觸發時,事件源會將事件分發到Handler(處理器),由Handler負責事件處理,Reactor模式中的反應器角色類似于事件驅動模式中的事件分發器(Dispatcher)角色,

具體來說,在Reactor模式中有Reactor和Handler兩個重要的組件:

  • Reactor:負責查詢IO事件,當檢測到一個IO事件時將其發送給相應的Handler處理器去處理,其中,IO事件就是NIO中選擇器查詢出來的通道IO事件,
  • Handler:與IO事件(或者選擇鍵)系結,負責IO事件的處理,完成真正的連接建立、通道的讀取、處理業務邏輯、負責將結果寫到通道等,

從Reactor的代碼實作上來看,實作Reactor模式需要實作以下幾個類:

  • EventHandler:事件處理器,可以根據事件的不同狀態創建處理不同狀態的處理器,
  • Handler:可以理解為事件,在網路編程中就是一個Socket,在資料庫操作中就是一個DBConnection,
  • InitiationDispatcher:用于管理EventHandler,分發event的容器,也是一個事件處理調度器,Tomcat的Dispatcher就是一個很好的實作,用于接收到網路請求后進行第一步的任務分發,分發給相應的處理器去異步處理,來保證吞吐量,
  • Demultiplexer:阻塞等待一系列的Handle中的事件到來,如果阻塞等待回傳,即表示在回傳的Handler中可以不阻塞的執行回傳的事件型別,這個模塊一般使用作業系統的select來實作,在Java NIO中用Selector來封裝,當Selector.select()回傳時,可以呼叫Selector的selectedKeys()方法獲取Set,一個SelectionKey表達一個有事件發生的Channel以及該Channel上的事件型別,

接下來,我們便從具體的常見來一一探討一下Reactor模式下的各種執行緒模型,

從一定意義上來說, 基于Reactor模式的Reactor模型是非阻塞I/O模型,

2.0. 單Reactor單執行緒模型

單Reactor單執行緒模型主要是指將服務端的整個處理事件分為若干個事件,Reactor 通過事件檢測機制把若干個事件Handler分發給不同的處理器去處理,簡單來說,Reactor和Handle都放入一個執行緒中執行,

v8lGdK.png

在實際作業中,若干個客戶端連接訪問服務端,假如會有接收事件(Accept Event),讀事件(Read Event),寫事件(Write Event),以及執行事件(Process Event)等,其中,:

  • Reactor 模型則把這些事件都分發到各自的處理器,
  • 整個程序,只要有等待處理的事件存在,Reactor 執行緒模型不斷往后續執行,而且不會阻塞,所以效率很高,

由此可見,單Reactor單執行緒模型具有簡單,沒有多執行緒,沒有行程通信,但是從性能上來說,無法發揮多核的極致,一個Handler卡死,導致當前行程無法使用,IO和CPU不匹配,

在Java領域中,對于一個單Reactor單執行緒模型的實作,主要需用到SelectionKey(選擇鍵)的幾個重要的成員方法:

  • void attach(Object o):將物件附加到選擇鍵,可以將任何Java POJO物件作為附件添加到SelectionKey實體,
  • Object attachment():從選擇鍵獲取附加物件,與attach(Object o)是配套使用的,其作用是取出之前通過attach(Object o)方法添加到SelectionKey實體的附加物件,這個方法同樣非常重要,當IO事件發生時,選擇鍵將被select方法查詢出來,可以直接將選擇鍵的附件物件取出,

因此,在Reactor模式實作中,通過attachment()方法所取出的是之前通過attach(Object o)方法系結的Handler實體,然后通過該Handler實體完成相應的傳輸處理,

綜上所述,在Reactor模式中,需要將attach和attachment結合使用:

  • 在選擇鍵注冊完成之后呼叫attach()方法,將Handler實體系結到選擇鍵,
  • 當IO事件發生時呼叫attachment()方法,可以從選擇鍵取出Handler實體,將事件分發到Handler處理器中完成業務處理,

從一定意義上來說,單Reactor單執行緒模型是基于單執行緒的Reactor模式,

2.1. 單Reactor多執行緒模型

單Reactor多執行緒模型是指采用多執行緒機制,將服務端的整個處理事件分為若干個事件,Reactor 通過事件檢測機制把若干個事件Handler分發給不同的處理器去處理,

v8l8Z6.png

單Reactor多執行緒模型是基于單執行緒的Reactor模式的結構,將其利用執行緒池機制改進多執行緒模式,

相當于,Reactor對于接收事件(Accept Event),讀事件(Read Event),寫事件(Write Event),以及執行事件(Process Event)等分發到各自的處理器時:

  • 首先,對于耗時的任務引入執行緒池機制,事件處理器自己不執行任務,而是交給執行緒池來托管,避免了耗時的操作,
  • 其次,雖然Reactor只有一個執行緒,但是也保證了Reactor的高效,

在Java領域中,對于一個單Reactor多執行緒模型的實作,主要可以從升級Handler和升級Reactor來改進:

  • 升級Handler:既要使用多執行緒,又要盡可能高效率,則可以考慮使用執行緒池,
  • 升級Reactor:可以考慮引入多個Selector(選擇器),提升選擇大量通道的能力,

總體來說,多執行緒版本的Reactor模式大致如下:

  • 將負責資料傳輸處理的IOHandler處理器的執行放入獨立的執行緒池中,這樣,業務處理執行緒與負責新連接監聽的反應器執行緒就能相互隔離,避免服務器的連接監聽受到阻塞,
  • 如果服務器為多核的CPU,可以將反應器執行緒拆分為多個子反應器(SubReactor)執行緒;同時,引入多個選擇器,并且為每一個SubReactor引入一個執行緒,一個執行緒負責一個選擇器的事件輪詢,這樣充分釋放了系統資源的能力,也大大提升了反應器管理大量連接或者監聽大量傳輸通道的能力,

由此可見,單Reactor單執行緒模型具有充分利用的CPU的特點,但是行程通信,復雜,Reactor承放了太多業務,高并發下可能成為性能瓶頸,

從一定意義上來說,單Reactor多執行緒模型是基于多執行緒的Reactor模式,

2.2. 主從Reactor多執行緒模型

主從Reactor多執行緒模型采用多個Reactor 的機制,將服務端的整個處理事件分為若干個事件,Reactor 通過事件檢測機制把若干個事件Handler分發給不同的處理器去處理,每一個Reactor對應著一個執行緒,

v8l1qx.png

采用多個Reactor實體的機制:
-主Reactor:負責建立連接,建立連接后的句柄丟給從Reactor,
-從Reactor: 負責監聽所有事件進行處理,

相當于,Reactor對于接收事件(Accept Event),讀事件(Read Event),寫事件(Write Event),以及執行事件(Process Event)等分發到各自的處理器時:

  • 由于接收事件是針對于服務器端而言的,連接接收的作業統一由連接處理器完成,則連接處理器把接收到的客戶端連接均勻分配到所有的實體中去,
  • 每一個Reactor 實體負責處理分配到該Reactor 實體的客戶端連接,完成連接時的讀寫操作和其他邏輯操作,

由此可見,主從Reactor多執行緒模型中Reactor實體職責分工明確,具有一定分攤壓力的效能,我們常見Nginx/Netty/Memcached等就是采用這中模型,

從一定意義上來說,主從Reactor多執行緒模型是基于多實體的Reactor模式,

2. 基于Proactor模式的Proactor模型

Proactor 模型是指在事件驅動的思想上,基于Proactor 的作業模式而設計的異步I/O模型(AIO 模型),一定程度上來說,可以說是被動模式I/O模型,

v8lJIO.png

無論是 Reactor,還是 Proactor,都是一種基于事件分發的網路編程模式,區別在于 Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式則是基于「已完成」的 I/O 事件,

相對于Reactor來說,Proactor 模型處理讀取操作的主要流程:

  • 應用程式初始化一個異步讀取操作,然后注冊相應的事件處理器,此時事件處理器不關注讀取就緒事件,而是關注讀取完成事件,
  • 事件分離器等待讀取操作完成事件,
  • 在事件分離器等待讀取操作完成的時候,作業系統呼叫內核執行緒完成讀取操作,并將讀取的內容放入用戶傳遞過來的快取區中,
  • 事件分離器捕獲到讀取完成事件后,激活應用程式注冊的事件處理器,事件處理器直接從快取區讀取資料,而不需要進行實際的讀取操作,

由此可見,Proactor中寫入操作和讀取操作基本一致,只不過監聽的事件是寫入完成事件而已,

在Java領域中,異步IO(AIO)是在Java JDK 7 之后引入的,都是作業系統負責將資料讀寫到應用傳遞進來的緩沖區供應用程式操作,

其中,從對于Proactor模式的設計來看,Proactor 模式的作業流程:

v84Emd.png

  • Proactor Initiator: 負責創建 Proactor 和 Handler 物件,并將 Proactor 和 Handler 都通過 Asynchronous Operation Processor 注冊到內核,
  • Asynchronous Operation Processor :負責處理注冊請求,并處理 I/O 操作,
  • Asynchronous Operation Processor :完成 I/O 操作后通知 Proactor,
  • Proactor :根據不同的事件型別回呼不同的 Handler 進行業務處理,
  • Handler: 完成業務處理,其中是通過CompletionHandler表示完成后處理器,

從一定意義上來說, 基于Proactor模式的Proactor模型是異步IO,

3. 基于Promise模式的Promise模型

Promise模型是基于Promise異步編程模式,客戶端代碼呼叫某個異步方法所得到的回傳值僅是一個憑據物件,憑借該物件,客戶端代碼可以獲取異步方法相應的真正任務的執行結果的一種模型,

vYLMQA.png

Promise 模式是開始一個任務的執行,并得到一個用于獲取該任務執行結果的憑據物件,而不必等待該任務執行完畢就可以繼續執行其他操作,

從Promise 模式的作業機制來看,主要如下:

  • 當我們開始一個任務的執行,并得到一個用于獲取該任務執行結果的憑據物件,而不必等待該任務執行完畢就可以繼續執行其他操作,
  • 等到我們需要該任務的執行結果時,再呼叫憑據物件的相關方法來獲取,

由此可以確定的是,Promise 模式既發揮了異步編程的優勢——增加系統的并發性,減少不必要的等待,又保持了同步編程的簡單性,

從Promise 模式技術實作來說,主要職責角色如下:

  • Promisor:負責對外暴露可以回傳 Promise 物件的異步方法,并啟動異步任務的執行,主要利用compute方法啟動異步任務的執行,并回傳用于獲取異步任務執行結果的憑據物件,
  • Promise :負責包裝異步任務處理結果的憑據物件,負責檢測異步任務是否處理完畢、回傳和存盤異步任務處理結果,
  • Result :負責表示異步任務處理結果,具體型別由應用決定,
  • TaskExecutor:負責真正執行異步任務所代表的計算,并將其計算結果設定到相應的 Promise 實體物件,

在Java領域中,最典型的就是基于Future模型實作的Executor和FutureTask,

由于Future模型存在一定的局限性,在JDK 1.8 之后,對Future的擴展和增強實作又新增了一個CompletableFuture,

當然,Promise模式在前端技術JavaScript中Promise有具體的體現,而且隨著前端技術的發展日趨成熟,對于這種模式的運用早已日臻化境,

寫在最后

Footer-Picture

在Java領域中,Java領域中的執行緒主要分為Java層執行緒(Java Thread) ,JVM層執行緒(JVM Thread),作業系統層執行緒(Kernel Thread),

從Java執行緒映射型別來看,主要有執行緒一對一(1:1)映射,執行緒多對多(M:1)映射,執行緒多對多(M:N)映射等關系,

因此,Java 領域中的執行緒映射模型主要有內核級執行緒模型(Kernel-Level Thread ,KLT)、應用級執行緒模型(Application-Level Thread ,ALT)、混合兩級執行緒模型(Mixture-Level Thread ,MLT)等3種模型,

在Java領域中,我們對照執行緒概念(單執行緒和多執行緒)來說,可以分為Java 執行緒-阻塞I/O模型和Java 執行緒-非阻塞I/O模型兩種,其中,

  • Java 執行緒-阻塞I/O模型: 主要可以分為單執行緒阻塞I/O模型和多執行緒阻塞I/O模型,
  • Java 執行緒-非阻塞I/O模型:主要可以分為應用層I/O多路復用模型和內核層I/O多路復用模型,以及內核回呼事件驅動I/O模型,

特別指出,在Java領域中,非阻塞I/O的實作完全是基于作業系統內核的非阻塞I/O,JDK會依據作業系統內核支持的非阻塞I/O方式來幫助我們選擇實作方式,

綜上所述,在Java領域中,并發編程中的執行緒機制以及多執行緒的控制,在實際開發程序中,需要依據實際業務場景來考慮和衡量,這需要我們對其有更深的研究,才可以得心應手,

在討論編程模型的時候,我們提到了像基于Promise模式和基于Thread Pool 模式的這樣的設計模式的概念,這也是一個我們比較容易忽略的概念,如果有興趣的話,可以自行進行查詢相關資料進行了解,

最后,祝福大家在Java并發編程的“看山是山,看山不是山,看山還是山”的修行之路上,“撥開云霧見天日,守得云開見月明”,早日達到有跡可循到有跡可尋的目標!

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

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/501758.html

標籤:其他

上一篇:設計模式——面向物件設計原則

下一篇:設計模式之外觀模式

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more