首頁  >  文章  >  Java  >  JAVA虛擬機器(JVM)詳細介紹(八)-高效率並發

JAVA虛擬機器(JVM)詳細介紹(八)-高效率並發

王林
王林轉載
2019-08-24 17:25:482285瀏覽

JAVA虛擬機器(JVM)詳細介紹(八)-高效率並發

記憶體模型

#記憶體模型是在特定的操作協議下,對特定的記憶體或高速緩存進行讀寫存取的過程抽象。其主要目標是定義程式中各個變數的存取規則。

#主記憶體與工作記憶體

JAVA虛擬機器(JVM)詳細介紹(八)-高效率並發

所有的變數都儲存在主內存中,每個線程還有自己的工作內存,其工作內存中是被線程使用到的變量的主內存副本拷貝,線程對變量的讀取、賦值等操作都必須在工作記憶體中進行,而不能直接讀取主記憶體中的變數。

記憶體間互動操作

從主記憶體拷貝到工作記憶體:順序地執行read和load操作。
工作記憶體同步到主記憶體:store和write操作。

volatile的特性

Volatile的作用和synchronized相同,但是和synchronized相比,更輕量。其特性主要有以下兩點:

保證此變數對所有執行緒的可見性

啥意思呢?指當一個執行緒修改了這個變數的值,新值對於其他執行緒來說是立即可知的。而普通變數做不到這一點,普通變數的值在線程間傳遞均需要透過主記憶體來完成,例如線程A修改了一個普通變數的值,然後向主記憶體進行回寫,另外一條線程B在線程A回寫完成了之後再從主記憶體進行讀取操作,新變數值才會對線程B可見。

禁止指令重新排序最佳化

因為指令重新排序會幹擾程式的並發執行。

多重執行緒

#為什麼需要多執行緒?

電腦的運算速度與它的儲存和通訊子系統速度的差距太大,大量的時間都花費在磁碟I/O、網路通訊、資料庫存取上了。使用多執行緒能更好地利用cpu。

有哪些並發應用程式場景?

充分利用電腦處理器

一個服務端同時對多個客戶端提供服務

#如何讓處理器內部的運算單元被充分利用?

加入一層高速緩存

#將運算需要使用到的資料複製到快取中,讓運算能快速進行。當運算結束後再從快取同步回記憶體中,這樣處理器就無須等待緩慢的記憶體讀取了。不過這個要考慮一個問題:怎麼保證快取的一致性。

JAVA虛擬機器(JVM)詳細介紹(八)-高效率並發

對輸入程式碼進行亂序執行最佳化

#執行緒的實作方式

#使用核心執行緒實作

核心執行緒就是直接由作業系統核心支援的執行緒。

使用使用者執行緒實作

使用者執行緒的建立、同步、銷毀和調度完全在使用者狀態中完成,不需要核心的幫助,核心也感知不到線程存在的實作。這種實現方式使用較少。

使用使用者執行緒加上輕量級進行混合實作

合併到一起

執行緒調度

#執行緒調度是指系統為執行緒分配處理器使用權的過程。主要分為兩種:協同式和搶佔式。

協同式

執行緒的執行時間由執行緒本身來控制,執行緒把自己的工作執行完了,會主動通知系統切換到另一個執行緒上。
其優點是實作簡單,而且沒有執行緒同步的問題。缺點是如果一個線程編寫有問題,並且一直不告訴系統進行線程切換,那麼程式就會一直阻塞在那裡,容易導致系統崩潰。

搶佔式

執行緒將由系統來指派執行時間,執行緒切換不由本身來決定。 java使用的執行緒調度方式就是這種。

執行緒安全性

當多個執行緒存取一個物件時,如果不考慮這個執行緒在執行階段環境下的調度和交替執行,也不需要進行額外的同步,或者在調用方進行任何其它的協調操作,調用這個對象的行為都可以獲得正確的結果,那麼這個對象就是安全的。

共享資料的分類


######################################## ####不可變的共享數據是用final修飾的數據,其一定是線程安全的。如果共享資料是一個基本類型變量,那麼只要在定義的時候使用final關鍵字即可。 ######如果共享資料是一個對象,那就需要對象的行為不會對其狀態產生影響,並且可以將對像中帶有狀態的變數都宣告為final。例如String類別就是一個不可變類別#########絕對線程安全######

在Java API中標註自己是線程安全的類,大多數都不是絕對的線程安全。例如Vector是一個執行緒安全的集合,它的所有的方法都被修飾成同步,但是在多執行緒的環境中,它依舊不是同步的。

相對執行緒安全性

#相對執行緒安全性就是我們通常意義上所說的執行緒安全,它只能保證對這個物件單獨操作是線程安全的。但是對於一些特定順序的連續調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。
大部分的線程安全類別都屬於這種類型。

執行緒相容

物件本身不是線性安全的,但可以透過在呼叫端正確地使用同步手段來確保物件在並發環境中可以安全的使用。大部分的不是線程安全的類,都屬於這種類型。

執行緒對立

#無論如何,都不能在多執行緒環境中並發使用,如System.setIn( )、System.SetOut()。一個對輸入進行修改,一個對輸出進行修改,兩者是不能「交替」進行的。

實作方法

#方式一:互斥同步-悲觀並發策略

(1)synchronized

其原理是:這個關鍵字在經過編譯後,會在同步區塊的前後分別形成monitorenter和monitorexit這兩個字節碼指令。當執行monitorenter指令時,程式會嘗試取得物件的鎖,如果能取得到,則把鎖的計數器 1,對應的,在執行monitorexit時,會將鎖定計數器-1。當計數器為0時,鎖就被釋放。

其特點是:對同一條執行緒來說是可重入的;同步區塊在已進入的執行緒執行完之前,會阻塞後面的其他執行緒進入。

其選用場景是:在確實必要的情況下才使用此,因為其是重量級的。

(2)ReentrantLock

此重入鎖定是java.util.concurrent(JUC)套件下的類別。其高階特性有:等待可中斷、可實現公平鎖、鎖可以綁定多個條件。

方式二:非阻塞同步-樂觀並發策略

#先進行操作,如果沒有其它執行緒爭用共享數據,那操作就是成功了;如果共享資料有爭用,產生了衝突,那就再採取其它的補償措施。

方式三:無同步方案

如果一個方法本來就不涉及共享數據,那就沒有必要進行同步措施。例如可重複程式碼和線程本地儲存。

(1)可重入程式碼

如果一個方法,它的回傳結果是可預測的,只要輸入了相同的數據,就都能傳回相同的結果,那它就滿足可重入的要求。

(2)執行緒本地儲存

如果一段程式碼中所需要的資料必須與其它程式碼共享,而且這些共享資料的程式碼在同一個執行緒中執行,如此,我們可以把共享資料的可見範圍限制在一個執行緒中,這樣,就不用同步也能保證執行緒之間不會出現資料爭用問題。

鎖定最佳化

適應性自旋

因為阻塞或喚醒一個JAVA的線程需要作業系統切換CPU狀態來完成,這種狀態的轉換需要耗費處理器時間。如果同步程式碼區塊中的內容過於簡單,很可能導致狀態轉換消耗的時間比使用者程式碼執行的時間還要長。

為了解決這個問題,我們可以讓後面請求鎖的線程“稍等一下”,執行一個忙循環,進行自旋。此時沒有放棄處理器的執行時間。如果自旋超過了限定的次數,仍然沒有成功獲得鎖,那就會使用傳統的方式去掛起線程了。

 那什麼叫做適應性自旋呢?

就是在同一個鎖定物件上,如果自旋等待剛剛成功獲得過鎖,那虛擬機器就會認為這次自旋獲得鎖的機率挺大,就會允許其自旋等待持續相對更長的時間。相反,如果自旋很少成功獲得過鎖,則可能省略掉自旋過程。

鎖定消除

#指虛擬機器即時編譯器在執行時,對一些程式碼上要求同步,但是被偵測到不可能存在共享資料競爭的鎖進行消除。

鎖定粗化

如果一系列的連續操作都對同一個物件反覆加鎖和解鎖,甚至加鎖操作是出現在循環體中的,那即使沒有執行緒競爭,頻繁地進行互斥同步操作也會導致不必要的性能損耗。
如果虛擬機器偵測到一串零碎的操作都對同一個物件加鎖,將會把加鎖同步的範圍粗化到整個操作序列的外部,這樣只需要加鎖一次就夠了。

#輕量級鎖定

#在沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互斥所產生的效能損耗。
適用場景:無實際競爭,多個執行緒交替使用鎖定;允許短時間的鎖定競爭。

偏向鎖定

#偏向鎖定用於減少無競爭且只有一個執行緒使用在鎖的情況下,使用輕量級鎖產生的效能消耗。輕量級鎖每次申請、釋放鎖都至少需要一次CAS,但偏向鎖只有初始化時需要一次CAS。
適用場景:無實際競爭,將來只有第一個申請鎖的執行緒會使用鎖定。

以上是關於JAVA虛擬機器中高效並發的詳細介紹,更多相關問題請訪問PHP中文網:JAVA影片教學

以上是JAVA虛擬機器(JVM)詳細介紹(八)-高效率並發的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除