接下來對鎖的概念再次進行深刻的介紹
以前反覆的提到鎖,一般的理解就是,鎖---互斥---同步---阻塞
其實這是經常使用的獨佔鎖(排它鎖)的概念,也是一種簡單粗暴的解決方案
抗戰電影中,常常出現爲了阻止日本人炸橋?炸路?的場景,這只是阻止日本人的一種手段,若是大喊一聲TMD滾蛋,日本人就走了,還炸橋幹嗎?
用鎖是爲了線程安全,而不是爲了上鎖,上鎖是一種途徑,獨佔鎖則是「上鎖」的其中一種形式
若是有更優雅的上鎖方式,天然沒必要要每次都簡單粗暴的使用獨佔鎖,不是嘛
從幾個維度能夠大體分爲下面幾種
分類是以鎖爲核心,而延展出來的優雅的使用方式
樂觀鎖與悲觀鎖
- 樂觀就像小米的宣傳語-永遠相信,美好的事情即將發生;
- 而悲觀就像透過有色眼睛看世界,永遠都帶有顏色;
對應到程序中
使用鎖是爲了什麼?由於線程之間會共享數據,共享數據就必定會出現問題嗎?固然是機率的
- 兩我的去同一個水井打水,若是每次都錯開,那麼美好一直存在
- 若是每天趕到同一個時間點,我相信遲早必有一戰.....
因此如何看待在多線程共享數據中,出現的機率性競爭問題?
- 樂觀的眼光就是堅信絕大多數時候是沒問題的,只須要最後發生修改或者操做時進行校驗,好比校驗是否被修改過,而後再去進一步處理
- 悲觀的眼光就是堅信確定會有問題,因此我就一直加鎖,只要一直鎖住反正確定不會出現問題
因此你看,樂觀鎖其實能夠並無鎖,是在邏輯上實現了鎖的業務
假設這樣一種比較極端的場景
A,B兩個線程,共享數據c
A線程每分鐘都會對c進行操做(一天24*60次),B天天都會對c進行一次操做
試想一下樂觀鎖和悲觀鎖之間的性能差別?
在這個場景中能夠認爲即便沒有任何防範措施,天天正確的機率也會大於99.9%(不考慮一次錯誤後致使的後續連鎖錯誤),若是選用悲觀鎖,將會有多少無謂的損耗?
synchronized就是悲觀鎖,他會爲你保障百分百的加鎖與同步
公平鎖與非公平鎖
公平與非公平的字面意思你們都很清晰,沒有人理解起來有難度
好比語文老師對你和同桌的態度一視同仁、好比領導對你和同事的年終獎你以爲不公平....
可是,對於鎖來講,這個公平與不公平針對的是什麼?
以前在監視器概念中提到的,線程若是在某個監視器的等待集合中,那麼若是當前線程執行結束後,誰應該被選做下一個進入監視器的線程呢?
這就是鎖的公平性針對的點
- 對於鎖的請求,每一個線程老是有一個先來後到的前後順序,若是按照前後順序,那麼就是公平鎖
- 若是不按照前後順序,隨機的或者按照什麼算法優先級等選擇,那麼就是非公平鎖
除非有額外的公平性要求,不然不該該使用公平鎖,由於對性能是有損耗的
獨佔鎖和共享鎖
- 若是一個鎖僅僅只能被一個進程擁有,那麼他就是獨佔的;
- 若是一個鎖能夠同時被多個線程擁有,那麼他就是共享的;
獨佔鎖會保障任什麼時候候都只是有一個線程進行訪問,ReentrantLock就是獨佔鎖,synchronized的原理也是獨佔鎖
而讀寫鎖ReadWriteLock就是共享鎖,能夠同時容許多個讀線程進行操做
可重入鎖
重入,就好像是你結帳後餐館老闆對你說的下次再來!
當一個線程想要獲取一個被其餘線程獨佔的鎖時,你須要等待,可是若是是本身已經得到的鎖呢?
答案是你能夠屢次獲取,這就是可重入
好比一個類中有兩個同步的實例方法,而鎖對象都是當前對象this
若是不可重入會發生什麼?
調用了A方法以後,想要調用方法B可是鎖卻被本身佔有了,若是不可重入,就成了本身等本身,豈不是傻子?
可重入鎖在內部維護了一個計數器,用於記錄重入次數
本身得到一次,那麼計數器+1,釋放一次計數器-1,若是計數器爲0,說明該線程釋放了該鎖,不然,鎖仍舊被該線程持有
自旋鎖
在以前線程簡介中有提到,Java線程是內核級映射的線程(1.2吧?以後)
若是一個線程請求獲取一個鎖時,並不能獲取到,那麼將會進入阻塞狀態,也就是會被切換到內核態而後掛起
當該線程獲取到鎖時,又須要切換到內核狀態進行喚醒,說白了須要用戶狀態與內核狀態的切換
並且,這個狀態的切換,仍是比較消耗性能的
怎麼辦?
有一種解決辦法就是線程繼續運行,過一下子再次嘗試鎖的獲取
怎麼作到的?
其實就是至關於CPU空跑,而不是直接將線程進行掛起
因此說至關於犧牲了CPU的時間片,換取內核狀態的開銷,這就涉及到一個平衡點的問題
若是線程之間競爭不激烈,可能下次的嘗試獲取就成功了
可是若是線程之間競爭很是激烈,下次仍是搶不到,下下次仍是搶不到......會發生什麼狀況?
那就是浪費了太多的CPU時間片,並且,你也不能永遠的像「死循環」同樣嘗試呀,因此通常會有一個時長或者次數,總之有個上限
如今對自旋鎖應該有了必定的瞭解了,自旋就是本身在那邊不停地打轉,不給糖吃就打滾的哭
書面點的說法:
當前線程獲取鎖時,若是發現鎖已經被其餘線程佔有,並不會立刻阻塞本身,在不放棄CPU的狀況下,屢次嘗試
剛纔也說明了,對於線程是否競爭激烈,自旋鎖有着不一樣的反應,也說不定會致使CPU白白浪費了時間片,因此要根據業務來
適應性自旋
剛纔說到,對於自旋不能無止境的,那就是相似「死循環」,因此都有限制,一般用次數,好比規定次數爲5
可是實際狀況中,可能有時常常1次就可以成功,也可能常常5次了尚未成功,若是1次就成功的還好,若是說限制爲5次,每次都5次後仍是失敗,這就是純粹的白忙活
因此後來出現了自適應的自旋鎖,自旋的次數(限制)再也不是固定的了
- 若是對於某個鎖,自旋不多成功得到過,那在之後嘗試獲取這個鎖時將可能省略掉自旋過程,直接阻塞線程,避免浪費處理器資源
- 若是對於某個鎖,自旋等待剛剛成功得到過鎖,那麼將會認爲此次也極可能是成功的,因此將會容許自旋,甚至容許更屢次數(時間)的自旋等待
對於適應性自旋,大體邏輯是這樣,具體細節由具體算法決定
再次強調
對於自旋,表面上看是減小了阻塞的發生,進而能夠減小狀態切換的性能損耗,可是這是以浪費CPU爲代價的
因此並不能認爲自旋鎖是治療百病的良藥
實際使用中,不能只看到帶來的好處,也須要關注付出的代價,而對於適應性自旋,只是對於自旋的「死板」的一種調優而已
小結
以上分類只是就某一個維度對鎖的概念以及應用的分析
他們概念上不是徹底隔離的,不是說就存在那麼幾種鎖,A,B,C....A就是A,B就是B
好比人類都是人,可是分爲男人、女人,還能夠分爲好人、壞人等,好人有男女,壞人也有男女
好比獨佔鎖屬於悲觀鎖,獨佔就是要保障同一時間只有一個線程操做,其餘線程必須等待
共享鎖就屬於樂觀鎖,由於他放寬了加鎖的條件
理解鎖的分類有助於後續關於其餘高級工具、類的理解與學習