大話java:兩程序員玩“鎖”,一人搶救無效身亡

房間裡燈光昏暗,兩個男人相對而坐,

良久,眼睛男率先打破僵局,

眼睛男,知道鎖麼

帥氣男,知道些,

眼睛男:什麼是鎖?(先發制人)

大話java:兩程序員玩“鎖”,一人搶救無效身亡

一種保護機制,在多線程的情況下,保證操作數據的正確性/一致性,

眼鏡男:有哪幾種分類?

悲觀鎖,樂觀鎖,獨佔鎖,共享鎖,公平鎖,非公平鎖,分佈式鎖,自旋鎖

眼睛男:講講樂觀鎖悲觀鎖吧(順藤摸瓜)

大話java:兩程序員玩“鎖”,一人搶救無效身亡

一般喜歡放在數據庫來講(其實這兩個概念是屬於計算機的,不要被誤導),就說mysql吧,悲觀鎖,主要是表鎖,行鎖還有間隙鎖,葉鎖,讀鎖,因為這些鎖在被觸發的時候勢必引起線程阻塞,所以叫悲觀

另外樂觀鎖其實在mysql本身中不存在的,但是mysql提供了種mvcc的機制,支持樂觀鎖機制,

眼睛男:mvcc是咋回事?(誘敵深入)

大話java:兩程序員玩“鎖”,一人搶救無效身亡

只是在innodb引擎下存在,mvcc是為了滿足事務的隔離,通過版本號的方式,避免同一數據不同事務間的競爭,所說的樂觀鎖只在事務級別為讀未提交讀提交,才會生效,

眼睛男:具體mvcc機制有什麼?(窮追不捨)

多版本併發控制,保證數據操作在多線程過程中,保證事務隔離的機制,可以降低鎖競爭的壓力,保證比較高併發量,這個過程。在每開啟一個事務時,會生成一個事務的版本號,被操作的數據會生成一條新的數據行(臨時),但是在提交前對其他事務是不可見的,對於數據的更新操作成功,會將這個版本號更新到數據的行中,事務提交成功,將新的版本號,更新到此數據行(永久)中,這樣保證了每個事務操作的數據,都是相互不影響的,也不存在鎖的問題;

眼睛男:那麼在多個事務(操作同一條數據)併發過程中,誰先成功?

mysql判斷,其實就是誰先提交成功算誰的

眼睛男:說到事務了,聊聊事務,

大話java:兩程序員玩“鎖”,一人搶救無效身亡

事務常說一系列操作作為一個整體要麼都成功要麼都失敗,主要特性acid,事務的的實現主要依賴兩個log redo-log,undo-log,每次事務都會記錄數據修改前的數據undo-log,修改後的數據放入redo-log,提出成功則使用redo-log 更新到磁盤,失敗則使用undo-log將數據恢復到事務之前的數據

眼鏡男,嗯,再說說獨佔鎖,共享鎖吧(轉移陣地)

(嗯,獨佔,共享,公平,非公平,自旋鎖這些都是廣泛的概念,很多語言都有,包括操作系統,js的同學請回避)

獨佔鎖很明顯就是持鎖的線程只能有一個,共享鎖則可以有多個

眼睛男:獨佔可以理解,共享的意義在哪裡?

共享鎖是為了提高程序的效率,舉個例子數據的操作有讀寫之分,對於寫的操作加鎖,保證數據正確性,而對於讀的操作如果不加鎖,在寫讀操作同時進行時,讀的數據有可能不是最新數據,如果對讀操作加獨佔鎖,面對讀多寫少的程序肯定效率很低,所有就出現了共享鎖,對於讀的的操作就使用共享的概念,但是對於寫的操作則是互斥的,保證了讀寫的數據操作都一致,在java中上述的鎖叫讀寫鎖

眼睛男:讀寫鎖的機制是什麼呢?(佯攻)

大話java:兩程序員玩“鎖”,一人搶救無效身亡

在java中讀寫鎖(ReadWritelock)的機制是基於AQS的一種實現,保證讀讀共享,讀寫互斥,寫寫互斥,如果要說機制的話,還要從AQS說起,這是java實現的一種鎖機制,互斥鎖,讀者寫鎖,條件產量,信號量,柵欄的都是它的衍生物,主要工作基於CHL隊列,voliate關鍵字修飾的狀態符stat,線程去修改狀態符成功了就是獲取成功,失敗了就進隊列等待,等待喚醒,AQS中還有很重要的一個概念是自旋,在等待喚醒的時候,很多時候會使用自旋(while(!cas()))的方式,不停的嘗試獲取鎖,直到被其他線程獲取成功

共享與獨佔的區別就在於,CHL隊列中的節點的模式是EXCLUSIVE還是SHARED,當一個線程成功修改了stat狀態,表示獲取了鎖,如果線程所在的節點為SHARED,將開始一個讀鎖傳遞的過程,從頭結點,向隊列後續節點傳遞喚醒,直到隊列結束或者遇到了EXCLUSIVE的節點,等待所有激活的讀操作完成,然後進入到獨享模式(這部分盡力了,大家還是看源碼)

公平與非公平的區別就在於線程第一次獲取鎖時,也就是執行修改stat操作時,是進隊列還是直接修改狀態,這是基本的工作機制,詳細的估計可以再聊好幾集

眼睛男:java 除了AQS 還有其他的鎖支持麼(佯攻未遂,尋找突破)

大話java:兩程序員玩“鎖”,一人搶救無效身亡

在java中,synchronized關鍵字,是語言自帶的,也叫內置鎖,synchronized關鍵字,我們都知道被synchronized修飾的方法或者代碼塊,在同一時間內,只允許一個線程執行,是明顯的獨享鎖,synchronized的實現機制?可以參考AQS的實現方式,只是AQS使用顯示的用lock.lock()調用,而sync作為關鍵字修飾,你可以認為在synchronized修飾的地方,自動添加了lock方法,結束的地方進行了unlock釋放鎖的方法,只是被隱藏了,我們看不到。

它本身實現有兩部分:monitor對象,線程,工作機制還是線程搶佔對象使用權,對象都有自己的對象頭,存儲了對象的很多信息,其中有一個是標識被哪個線程持有,對比AQS,線程從修改stat,變為修改monitor的對象頭,線程的等待區域動 AQS中的隊列,變為monitor對象中的某個區域,

大話java:兩程序員玩“鎖”,一人搶救無效身亡

眼睛男:能細說麼?(貼臉)

導演,他自己加戲

導演:按照劇本來

鎖一直是圍繞線程安全來實現的,比如獨佔鎖,它在內存裡面的操作是怎麼樣的

這個地方涉及到一個概念,內存模型(這個和jvm不要混淆,The Java memory model used internally in the JVM divides memory between thread stacks and the heap. This diagram illustrates the Java memory model from a logic perspective),是JVM用來區別線程棧和堆的內存方式,每個線程在運行的時候,所操作的數據存儲空間有兩個,一個是主內存 一個是工作內存,主內存其實就是jvm中堆,工作內存就是線程的棧,每次的數據操作,都是從主內存中把數據讀到工作內存中,然後在工作內存中進行各種處理,如果進行了修改,會把數據回寫到主內存,然後其他線程又進行同樣的操作,就這樣數據在工作內存和主內存,進進出出,不亦樂乎,在多次的情況下,就是因為進進出出的順序亂了,不是按照線程預期的訪問順序,就出現了數據不一致的問題,導致了多線程的不安全性;整個操作過程還牽涉到CPU,高速緩存等概念,略過。。。。

眼睛男:內存模型 還有哪些可以聊聊的(我們是抱著學習的心態)

happen-befor 原則,Volatile 關鍵字(線程的可見性),內存屏障

眼睛男:哦(慫了慫了)

大話java:兩程序員玩“鎖”,一人搶救無效身亡

happen-befor原則定義了內存模型執行過程中的定律,就像1+1 = 2,不可能被打破的jvm的運行機制都依賴於這個原則,是jvm的憲法!!!

Volatile關鍵字就有點叼了,Volatile修飾的數據,在被某個線程修改後,會被及時的回寫到主內存,然後其他線程再獲取時,就是新的數據,聽起來很美好,但是Volatile沒有辦法控制線程的順序,當一個數據(新數據)即將被修改到主內存時,剛好,另外一個線程從主內存讀了數據(老數據),並又進行了一波操作,又將數據(更新的數據)回寫到了主內存,整個過程(新數據)完全沒有起到一毛錢作用,最終導致了數據的錯誤,呼呼打完收工!!!!

大話java:兩程序員玩“鎖”,一人搶救無效身亡

眼鏡男:你為啥知道這麼多

因為我帥啊,

眼鏡男:有多帥?

可以用微笑殺死你

眼鏡男:來啊!

大話java:兩程序員玩“鎖”,一人搶救無效身亡

眼睛男猝 享年28歲!!!

。。。。。end。。。。。。

整個文章以“鎖”為半徑,構建了一個簡單的知識體系,直觀的感受一下

大話java:兩程序員玩“鎖”,一人搶救無效身亡


分享到:


相關文章: