Java併發編程鎖之獨佔公平鎖與非公平鎖比較

Java併發編程鎖之獨佔公平鎖與非公平鎖比較

公平鎖和非公平鎖理解:

在上一篇文章中,我們知道了非公平鎖。其實Java中還存在著公平鎖呢。公平二字怎麼理解呢?和我們現實理解是一樣的。大家去排隊本著先來先得到的原則,在排隊中,無論身份貴賤,一律平等對待。這是就是我們現實生活中的公平。大家都喜歡公平的。但是在Java中默認是非公平的,為什麼呢?

本文主要內容:公平鎖的現實生活理解;公平鎖演示;為什麼Java中默認是非公平鎖(公平鎖的非公平鎖的比較)

本篇是《凱哥(凱哥Java:kagejava)併發編程學習》系列之《Lock系列》教程的第四篇:《Java併發包下鎖學習第五篇:公平鎖理解及與非公平鎖的比較》。

生活中的例子:

同樣還是去ATM機取錢的例子。假設現在有3個人使用ATM取錢。路人甲不會用ATM,自己摸索耗時5min,然後終於學會怎麼使用了,但是密碼又忘掉了。打電話給家裡人諮詢耗時1min.當路人甲操作完成之後,後面兩個人排隊接著依次操作,這種方式是誰先到誰先操作,操作完成之後下一個人才可以操作的,不管貧富貴賤,不管你是取100還是取1W,取1W的人在取100的人後面,就要排著隊等著,這種看上去很公平的,無論貴賤,大家依次操作,這種操作模式站在多線程併發角度來看的話,就是公平鎖操作。

在路人甲總耗時6min之後,路人乙和路人丁兩個人操作耗時3min。也就是三個人總耗時9min.為什麼會產生這種情況呢?因為路人甲堵著了。一直佔著鎖的資源。在路人甲操作的過程中,其他人只能排隊等待。如果路人甲不會操作,排在他後面的路人丙插隊詢問路人甲,自己可以先插隊操作ATM,同時教路人甲。如果甲同意,則丙可以操作完成後,甲可以學著別人操作,有可能路人甲2min也能操作完成。這個時候,三個人總耗時就是5min了;如果甲不同意丙插隊操作,那麼丙只能回到原來位置上,排隊等待。這種操作模式站在多線程併發角度來考慮的話,路人甲在模式及和家人通話耗時看著是CPU切換上下文的耗時。路人丙插隊後獲取ATM資源,這個操作可以看著是非公平的,因為丙的進入時間比路人乙晚,但是丙先操作了。但是從最後三人總耗時來看,路人丙插隊,是的效率提高了。這種操作在Java併發中,稱之為非公平鎖。

需要說明的是,無論是顯式鎖還是隱式鎖默認都是非公平的。因為非公平能夠提升系統的吞吐量。

非公平鎖的定義:

線程先嚐試著獲取同步狀態操作(丙先嚐試著插隊),如果獲取到,就對共享變量操作(甲同意丙的插隊,丙就操作ATM機),如果獲取不到,就接著排隊。

使用方法二:獨佔公平演示

需求:控制檯打印的結果和線程順序一致。代碼如下:

Java併發編程鎖之獨佔公平鎖與非公平鎖比較

此時代碼和上一次代碼唯一區別如上圖:在實例化lock的是有個參數,設置了true.

運行結果:

Java併發編程鎖之獨佔公平鎖與非公平鎖比較

從運行結果中,我們發現控制檯打印出的獲取鎖的順序和調用鎖的時候順序是一樣的, 已經達到我們預期結果了。但是,還是每次只有一個線程操作,等這個線程操作完成釋放鎖後,其他線程才可以接著獲取鎖。

公平鎖與非公平鎖的比較

問題:

為什麼併發大師Doug Lea把ReentrantLock設計默認模式是非公平的?

其實要回答這個問題,就需要從公平鎖與非公平鎖的不同來進行比較了。我們先來看看ATM機操作在公平鎖和非公平鎖的場景下,如下圖:

Java併發編程鎖之獨佔公平鎖與非公平鎖比較

公平鎖:大家都排隊,如果一個線程堵著了(路人甲),其他線程只能等待這個。最終,三個線程操作完成,總耗時9min.

非公平鎖情況下:多個線程操作的共享資源的時候,發現共享資源還沒有被鎖定(路人甲還在摸索過程),就嘗試插隊(路人丙嘗試和甲溝通,先插隊操作並教會甲),如果插隊成功(甲同意了),就操作共享資源(丙先操作ATM機);如果插隊失敗(甲不同意),接著排隊(丙回到隊伍中排對)。如果插隊成功,最終耗時:5min.

從中我們可以看出公平鎖和非公平鎖的優缺點了。

優缺點比較:

非公平鎖:

優點:效率高;缺點:容易導致線程"飢餓"。當多個線程使用非公平的話,有可能有一個線程一直就獲取不到競爭權,導致這個線程會"飢餓而死"。

適用場景:

如果在不考慮TPS(單位時間內成功完成的次數)作為唯一考量指標的場景下,可以使用非公平鎖來操作,因為非公平鎖能提高系統的吞吐量;

公平鎖:

優點:避免了線程的"飢餓";缺點:性能相對於公平鎖會差很多。

Java併發編程鎖之獨佔公平鎖與非公平鎖比較


分享到:


相關文章: