面試話癆(三)我會鎖的四種配法,您配嗎?

  面試話癆系列是從技術廣度的角度去回答面試官提的問題,適合萌新觀看!html

  面試官,我知道一種在走路時不被鎖絆倒的方法,你想聽嗎?java


   鎖相關的知識,咱們能夠記住下面這個知識網,這樣雖然不可能保證全部的問題都會,至少能開開心心的跟面試官聊個一瓶礦泉水的時間。面試

1、爲啥要有多線程?redis

  由於CPU太快了。CPU是一個電學組件,它在大部分時間的運算速度都是光速。而大部分的外接設備,如:數據庫

  1. 打印機、音響、鍵盤、鼠標還停留在物理速度這一層。數組

  2. 機械硬盤之因此叫機械,也是由於用了物理的磁頭,讀取數據時還須要用馬達轉動磁頭。安全

  3. 錯綜複雜的網絡環境。性能優化

  這些設備對於CPU來講太慢了。因此就有了CPU的時間片輪轉制度。CPU能夠處理完打印機的運算,接着再去處理其餘的事,其餘的事情處理完之後,再回來看打印機處理完沒有。服務器

2、如何支持多線程?(Java內存模型)網絡

  因此,支持多線程是必須的。Java內存模型體現了多線程的支持原理(Java內存模型和JVM內存模型不是一個東西,詳細區分傳送門:面試話癆(四)常量在哪裏呀,常量在哪裏)。

  

   大致的操做流程是,從共享的存儲空間中複製一份本身須要的數據到線程獨享的工做空間,對該數據進行一系列的運算後,再覆蓋掉共享空間中本來的數據。整個操做按順序分爲6個步驟:read,load,use,assign,store,write。另外還容許使用lock,unlock鎖住共享空間中的數據,避免其餘線程同時操做一個變量。這八個操做是最基礎的不可拆分的操做,所以被稱爲原子操做。

  在沒有使用lock上鎖的前提下,多個線程能夠同時讀取一個變量,而後在各自的空間中同時運算。這就是Java支持多線程的方式。

3、多線程會有啥問題?

  就好像炒菜同樣,若是幾我的幾個鍋一塊兒炒,不免會手忙腳亂出點錯。出錯的方式主要是三方面:

  1. 不可見性。炒菜以前,咱們不會每次都去確認醬油瓶中是否還有醬油,咱們會根據上一次的使用經驗默認還有醬油,然而這個醬油可能已經被別人用光了。專業術語就是,線程從共享內存中read出數據並load到獨享的工做內存後,use都是直接使用工做內存中的數據,而不會去管共享內存中的數據是否已經被改變。若是其餘線程同時修改了這個數據,那麼就會形成多線程的錯誤。

  2. 無序性。由於炒菜的過程當中過於慌亂,可能會先下肉,下完肉纔想起忘記加油。處理器也會調整指令的順序,固然不是由於處理器慌亂,而是處理器自己的一種性能優化,被稱作指令重排。咱們小學時應該都作過一道數學題:已知小明寫做業須要10分鐘,淘米須要2分鐘,煮飯須要30分鐘,洗衣服須要10分鐘,問小明作完這些事至少須要多少秒?咱們會經過調整作事的順序,讓小明在儘可能短的時間內,完成儘可能多的事情。處理器在處理這8種原子操做時,也是會經過指令重排,在儘可能短的時間內完成儘可能多的事情。過分的指令重排可能就會形成多線程的錯誤。

  3. 非原子性。假如你在作湯時須要加入大量的小米辣,你一次只拿的走一半,因此你先拿了一半,走到鍋邊,放入小米辣後再回來取另外一半,在這個過程當中另外一半可能已經挪用或者替換了。在java內存模型中也會存在這種狀況。基本的8種操做都是原子性的,可是他們操做大數據時,可能就會出現非原子性的問題。好比read一次只能從共享內存中讀取32位的數據,而double和long佔據了64位的空間,這時的讀取就是非原子性的(不少虛擬機已經優化了這個問題,針對64位的long和double執行原子性的操做,但對於更大的數據對象,好比HashMap,它的讀取仍是非原子性的)。

4、如何解決多線程的問題?

  三中已經指出了多線程可能存在的問題,那麼反過來講,保證多線程的可見性、有序性、原子性就能保證線程的安全。

  原子性是指操做是密不可分一次完成的,這屬於放棄多線程去達到絕對的安全。有序性是指抑制處理器的指令重排,保證read、load、use、assign、store、write的讀寫順序。至於可見性,咱們這裏須要着重講一下:

  有的人會認爲,若是不一樣線程間對數據的修改是第一時間相互可見的,那就應該能保證每次想處理數據的都是最新的數據,那應該就沒有多線程的問題了!

  其實不對,可見的意思是 當你想看的時候,就能看見最新的值。「當你想看」 這是一個主動的過程,並非一個被動接收的過程。也就是說別的線程修改完值後,並不會主動通知到其餘線程。須要其餘線程本身去獲取最新的值。可見性只能保證每次線程想獲取的值是最新的值,不能保證已經獲取到的值會隨着其餘線程的修改而修改。

  volitale就能保證操做的有序性和可見性,在必定程度上實現了多線程的安全。但要想徹底確保線程安全,還得加鎖。經常使用的加鎖方式有 同一個服務內部使用synchronized、分段式鎖、樂觀鎖,不一樣服務間可使用分佈式鎖。

  其實這個java內存模型沒有想象的那麼陌生,它跟咱們用過的數據庫沒啥區別。共享的內存空間就是數據庫服務器,每個創建的數據庫連接就是一個線程,咱們的SELECT的語句就至關因而read+load,use和assign就是咱們本身寫的業務相關的邏輯語句,INSERT、UPDATE、DELETE就是store+write。一樣數據庫也須要保證數據的原子性、隔離性、持久性,最終實現一致性。數據庫的鎖跟java中的鎖也一模一樣。好比將有索引的字段做爲限制條件進行UPDATE時,只會鎖住某些列,這至關因而分段式鎖,另外數據庫也有樂觀鎖悲觀鎖的概念。若是對數據庫鎖比較熟悉,在面試過程當中可使用類比的方法介紹java內存模型。面試官問:什麼是java內存模型。你先回一句:java內存模型其實跟常用的數據庫差很少。在大多數面試官眼中都是能加分的。畢竟再難的內存模型百度上也有,背1000個也不能表明什麼,相互比對觸類旁通,才能證實你是一個有想法,會創新的人。

  下面就講講四種經常使用的鎖實現方式。

5、鎖之間的差別

  synchronized是最直接的加鎖方式,被鎖住的對象只能同時被一個線程進行讀和寫。hashtable就是經過synchronized實現的。和synchronized相似的還有個lock。他是一個接口類,和原子操做中的lock不是一個概念。他與synchronized的區別以下

  synchronized lock
本質 Java關鍵字 接口類
結束方式 執行完相應的代碼,用完相應的變量後自動釋放鎖 須要調用unlock主動釋放鎖
是否公平 非公平鎖 公平鎖、非公平鎖可設置
讀寫鎖分離 未分離 容許鎖讀、鎖寫分別設置
鎖的對象 類、對象、代碼塊 代碼塊
喚醒方式 隨機喚醒一個線程或者喚醒所有 可喚醒指定線程

  上段中提到了hashtable,相應的就能想到CurrentHashMap。CurrentHashMap使用了更合理的分段式鎖,對性能進行了優化。注意下,全部的性能優化均可以歸結爲一句話:提升空間複雜度,下降時間複雜度,天下沒有免費的午飯你想獲得一件東西的時候,就要付出另一件東西。在jdk1.7中,CurrentHashMap就是在原來的數組+鏈表的基礎上,再加了一層數組。第一層的數組用於將數據分紅不一樣的段,每個段上都維護一個單獨的鎖,這樣就容許多個線程同時操做不一樣的段。集合的數據量越大,第一層的分段越多,性能提高就越明顯。再往下的具體實現我以爲不必再背了,由於你可能尚未背完,這個結構已經被改了。不信咱們看jdk1.8。

  咱們都知道,jdk1.8中,HashMap已經被優化成了size>64而且在某個長度爲8的鏈表上想要再添加的數據時,鏈表會被優化成紅黑樹,CurrentHashMap也改爲了同樣的結構。另外,分段式鎖也被改爲了樂觀鎖。

  樂觀鎖顧名思義,重點就突出一個字:樂觀。處理器樂觀的以爲,這個數據不會被其餘線程修改,因此會直接讀取數據進行操做,操做完要寫入的時候再去根據標識符,判斷共享內存中的值是否真的沒有被修改,沒有的話就順利插入。這裏的標識符通常來講都是一個不斷自增的版本號。

  也有說若是直接使用一個反覆被更改的0、1做爲標識符,這樣可能就會出現ABA(010)問題。即獲取0後標識符被其餘線程修改了兩次,致使標識符又回到了最初的0,這時候值就會被直接插入,形成多線程問題。可是兩種標識符並存的前提是,他們對比起來有不一樣的優缺點,所以能適用於不一樣的狀況。在我看來,使用自增版本號只有優勢,因此我以爲這個ABA問題應該只是個歷史遺留知識點,應該沒啥技術棧會出這種問題了。

  若是樂觀鎖在插入時遇到了不樂觀的狀況怎麼辦?有種辦法,就是從新拉取數據和版本號,重複進行一次樂觀的計算之後再去對比版本號,如此反覆,直到版本號沒被其餘線程改過之後,放入值。這個過程很像一個不停自旋的水渦,因此這種鎖被叫作自旋鎖,大名鼎鼎的CAS也就是這個原理。

  根據自旋鎖的原理,咱們也能相應的總結出自旋鎖適用的場景:讀的線程多,寫的線程少的場景。另外,自旋鎖的原理挺簡單的,建議本身在腦中想一想怎麼寫一個支持自旋的類出來。

  微服務多實例時也須要用到鎖。好比微服務中有一個定時任務,啓了多實例時,須要一個分佈式鎖去控制只讓其中一個實例執行定時任務。我目前就用過redis的分佈式鎖。直接調用redisTemplate的setIfAbsent方法,返回true則代表插入成功獲取到了鎖,不然代表已有其餘服務執行了該任務。記住setIfAbsent還提供了設置過時時間參數,必定要設置值,避免服務崩潰時形成死鎖。


   原創不易,轉載請註明出處

 

   本系列連接以下:

 

  面試話癆(一)讓咱們來熱切的討論這個養豬場吧

  面試話癆(二)C:JAVA String,別覺得你穿個馬甲我就不認識你了

  面試話癆(三)我會鎖的四種配法,您配嗎?

相關文章
相關標籤/搜索