聲明本文章只是對鎖理解的個人理解點。
背景
在編程的世界鎖是在處理高并發(fā)多線程可以說必須用到的機(jī)制或者說手段。首先要清楚的了解知道為什么要用鎖?在面試過程中或者在實際開發(fā)過程中都在說線程安全 并發(fā)安全這些安全到底是指的什么?
核心秘訣-鎖的共性(重點)
**在實際開發(fā)過程中鎖核心要點就是鎖的共性問題。找到了鎖的標(biāo)識就可以更好的去理解這個技術(shù)點核心。帶著三個疑問去看看syn、lock、redis、zk是怎么設(shè)計的。
- 鎖標(biāo)識是什么
- 操作鎖的原子操作
- 誰擁有鎖**
為了解決什么問題?
其實線程安全也好,還是并發(fā)安全。都是為了解決我們的數(shù)據(jù)安全。就是在多線程處理的時候,數(shù)據(jù)值能按照程序設(shè)計的去執(zhí)行的這么一個過程。設(shè)定這么一個場景,廁所坑位門鎖。當(dāng)占用這個坑位的時候,就只能一個人使用。怎么確定這個坑位是否有人?就得需要一個標(biāo)識讓其他人感知到。這個標(biāo)識就是鎖標(biāo)識。
鎖的原理
在編程上同樣也是如此。syn關(guān)鍵字、JUC下面的aqs框架、redis分布式鎖、zk分布式鎖、在這些鎖上你都能找到鎖標(biāo)識的這個影子。syn 是mark word 對象中的鎖標(biāo)志位、aqs隊列同步器獨享鎖、共享鎖的 state值、redis 分布式鎖的 setnx命令設(shè)置的key標(biāo)識。zk分布式鎖的node臨時對象。
只講述鎖的通用設(shè)計原理,詳細(xì)細(xì)節(jié)需要看一下代碼
synchronized
syn鎖的標(biāo)識存在對象頭的Mark Word中。舉例說一下(鎖的標(biāo)識符為了好理解自己定義的)
- 1、 當(dāng)線程1請求獲取鎖的時候發(fā)現(xiàn)沒有設(shè)置鎖,線程1 拿到鎖,鎖的標(biāo)識從0 變成1 設(shè)置owenr(為了知道誰進(jìn)去占了廁所的坑位)為當(dāng)前線程ID。
- 2 、當(dāng)線程1再次請求獲取鎖的時候發(fā)現(xiàn)偏向鎖再次獲取鎖。發(fā)現(xiàn)是偏量鎖,對比owenr的id發(fā)現(xiàn)是同一個線程直接不進(jìn)行鎖操作。這里就是鎖的重入性。
- 3、當(dāng)線程1 還沒有執(zhí)行完畢。線程2嘗試獲取鎖。發(fā)現(xiàn)已經(jīng)上鎖了,不是自己上的鎖。那就只能在門口等待。鎖就開始變成輕量級鎖。
- 4、當(dāng)線程3又來的時候發(fā)現(xiàn)這個坑位還是線程1占有。發(fā)現(xiàn)線程2也在等待呢。這個時候鎖的狀態(tài)就變成重量級鎖。因為2和3開始煎熬的等待了。2之前是在等待,1用完馬上就輪到自己了,發(fā)現(xiàn)3來了以后這回完嘍。2和3要等1用完之后搶坑位。本來就憋著呢,又來一個搶占坑位的。
這個是syn底層鎖的一個流程。當(dāng)然里面還有更加詳細(xì)的解釋介紹。但是這里不做解釋。
共性
- 鎖標(biāo)識是 makr word
- 操作鎖的原子操作
- 誰加的鎖 owenr
JUC下的AQS
在JUC下面的主要使用的是AQS,真正底層鎖的實現(xiàn)使用的是CAS鎖 對比、交換。這個是jvm底層提供的unsafe類的compareAndSwapInt方法設(shè)置的state值
AQS框架其實提供了一個state標(biāo)識。在利用CAS原子操作進(jìn)行獨占鎖和共享鎖。
* 獨占鎖 線程修改AQS的state值,標(biāo)記為獨占鎖
* 共享鎖 共享鎖其實是使用了隊列去完成了整個操作。
* 主要應(yīng)用 信號量 柵欄鎖、等其他的地方
共性
- 鎖標(biāo)識是AQS 的state值
- 操作鎖的原子 CAS
- 誰加的鎖 當(dāng)前線程
zk的分布式鎖
使用zookeeper創(chuàng)建臨時序列節(jié)點來實現(xiàn)分布式鎖,適用于順序執(zhí)行的程序,大體思路就是創(chuàng)建臨時序列節(jié)點,找出最小的序列節(jié)點,獲取分布式鎖,程序執(zhí)行完成之后此序列節(jié)點消失,通過watch來監(jiān)控節(jié)點的變化,從剩下的節(jié)點的找到最小的序列節(jié)點,獲取分布式鎖,執(zhí)行相應(yīng)處理,依次完成
解法二
zk節(jié)點唯一的! 不能重復(fù)!節(jié)點類型為臨時節(jié)點,線程1創(chuàng)建成功,線程2和線程3創(chuàng)建節(jié)點時候會報錯,該節(jié)點已經(jīng)存在。這時候線程2和線程3等待。線程1的程序現(xiàn)在執(zhí)行完畢,執(zhí)行釋放鎖。關(guān)閉當(dāng)前會話。臨時節(jié)點不復(fù)存在了并且事件通知Watcher,線程2和線程3繼續(xù)創(chuàng)建。
共性
- 鎖標(biāo)識是節(jié)點(path)
- 操作鎖的原子 zk的機(jī)制
- 誰加的鎖 服務(wù)器節(jié)點
redis的分布式鎖
redis的分布式鎖的實現(xiàn) zk的解法二 原理是一樣的。設(shè)置一個標(biāo)識(redis:key)
* 設(shè)置標(biāo)識
* 成功
* 獲取到鎖并設(shè)置過期時間
* 自動或超時釋放
* 失敗
* 自旋等待直到成功創(chuàng)建標(biāo)識
共性
- 鎖標(biāo)識是 Redis key標(biāo)識
- 操作鎖的原子 redis單線程原子操作
- 誰加的鎖 連接redis的操作線程
總結(jié)
其實不難發(fā)現(xiàn),鎖的核心理解就是在設(shè)置鎖標(biāo)識的一個過程。在學(xué)習(xí)理解鎖的過程就是找到共性的東西。就本篇文章而言就是找到了鎖的共性三點:
- 鎖標(biāo)識
- 原子性
- 誰持有鎖
希望大家能有自己總結(jié)實踐。畢竟學(xué)習(xí)要持續(xù)性的過程。以上是個人淺顯的總結(jié)。