1. 程式人生 > >Java鎖,真的有這麼複雜嗎?

Java鎖,真的有這麼複雜嗎?

前言

作者前面也寫了幾篇關於Java併發程式設計,以及執行緒和volatil的基礎知識,有興趣可以閱讀作者的原文部落格,今天關於Java中的兩種鎖進行詳解,希望對你有所幫助

本文受趙sir原創釋出,轉載請聯絡原創 https://blog.csdn.net/qq_36094018/article/details/90140209

為什麼使用synchronized

在上一章中說了volatile,在多執行緒下可以保證變數的可見性,但是不能保證原子性,下面一段程式碼說明:

執行上面程式碼,會發現輸出flag的值不是理想中10000,雖然volatile寫入時候會通知其他執行緒的工作記憶體值無效,從主記憶體重寫讀取。i++是三步操作,讀取-賦值-寫入不能保證原子性。原子性:不能被中斷要麼成功要麼失敗。

比如此時主記憶體的flag值10,執行緒1和執行緒2讀取到自己工作記憶體都是10,然後執行緒1在進行賦值的時候,執行緒2執行了,這時執行緒2發現自己記憶體的值和主記憶體的值一樣,並沒有修改,然後賦值寫入11,此時執行緒1執行,因為之前讀過了,會往下繼續執行寫入也是11。那麼兩個執行緒相當於只增加了一次。要想達到理想值,只需要修改public synchronized void increase() { flag++; }就行了。

什麼是synchronized

Java提供的一種原子性性內建鎖,Java每個物件都可以把它當做是監視器鎖,執行緒程式碼執行在進入synchronized程式碼塊時候會自動獲取內部鎖,這個時候其他執行緒訪問時候會被阻塞到佇列,直到進入synchronized中的程式碼執行完畢或者丟擲異常或者呼叫了wait方法,都會釋放鎖資源。在進入synchronized會從主記憶體把變數讀取到自己工作記憶體,在退出的時候會把工作記憶體的值寫入到主記憶體,保證了原子性。

synchronized機制

編譯後執行javap -v Test.class就會發現兩條指令。

在這裡插入圖片描述

synchronized是使用一種monitor機制,在進入鎖時候先執行monitorenter指令。退出的時候執行monitorexit指令。synchronized是可重入鎖,每個物件中都含有一個計數器當前執行緒再次獲取鎖,計數器+1,退出時候計算器-1,直到計數器為0才釋放鎖資源,喚醒其他執行緒來爭搶資源。任意一個物件都擁有自己的監視器,只有在執行緒獲取到監視器鎖時才會進入程式碼中,否則就進入阻塞狀態。

在這裡插入圖片描述

synchronized使用場景

  1. 對於普通方法,鎖是當前類例項物件。
  2. 對於靜態方法,鎖是當前類物件。
  3. 對於同步程式碼塊,鎖是synchronized括號裡的物件。

synchronized鎖升級

synchronized在1.6以前是重量級鎖,當前只有一個執行緒執行,其他執行緒阻塞。為了減少獲得鎖和釋放鎖帶來的效能問題,而引入了偏向鎖、輕量級鎖以及鎖的儲存過程和升級過程。在1.6後鎖分為了無鎖、偏向鎖、輕量鎖、重量鎖,鎖的狀態在多執行緒競爭的情況下會逐漸升級,只能升級而不能降級,這樣是為了提高鎖獲取和釋放的效率。

synchronized的鎖是存貯在Java物件頭裡的,如果物件是陣列型別,則虛擬機器用3個字寬(Word)儲存物件頭,如果物件是非陣列型別,則用2字寬儲存物件頭。1個字寬等於4個位元組。

在這裡插入圖片描述 Java物件頭中的Mark Word裡預設儲存了物件是HashCode、分代年齡、和鎖標記。

在這裡插入圖片描述 在執行的時候,Mark Word裡儲存的資料會隨著鎖標誌位的變化而變化,可能會變化為儲存以下四種形式。 在這裡插入圖片描述

偏向鎖

偏向鎖的意思未來只有一個執行緒使用鎖,不會有其他執行緒來爭取。

獲取鎖:

  1. 首先檢查Mark word中鎖的標誌是否為01。

  2. 如果是01,判斷物件頭的Mark word記錄是否為當前執行緒ID,如果是執行5,否則執行3.

  3. 執行緒ID並未只指向自己,傳送CAS競爭,如果競爭成功,則將Mark Word中執行緒ID設定為當前執行緒ID,執行5;如果未成功執行4。

  4. 當到達全域性安全點(在這個時間點上沒有正在執行的位元組碼)時獲得偏向鎖的執行緒被掛起,偏向鎖升級為輕量級鎖,然後被阻塞在安全點的執行緒繼續往下執行同步程式碼。

  5. 執行同步程式碼。 撤銷鎖:偏向鎖使用了一種等到競爭出現才釋放鎖的機制,所以當其他執行緒嘗試競爭偏向鎖時,持有偏向鎖的執行緒才會釋放鎖。需要等待全域性安全點,它首先暫停原持有偏向鎖的執行緒,然後檢查執行緒是否還在活著,如果執行緒處於未活動狀態,則釋放鎖標記,如果處於活動狀態則升級為輕量級鎖。

CAS

CAS全稱是Compare And Swap 即比較並交換,使用樂觀鎖機制,包含三個運算元 —— 記憶體位置(V)、預期原值(A)和新值(B)。 如果記憶體位置的值與預期原值相匹配,那麼才會將該位置值更新為新值 。否則,處理器不做任何操作。

輕量級鎖

執行緒在執行同步程式碼塊之前,JVM會先在當前執行緒的棧楨中建立用於儲存鎖記錄的空間,並將物件頭中的Mark Word複製到鎖記錄中,官方稱為Displaced Mark Word。

加鎖:

  1. CAS修改Mark Word,如果成功指向棧中鎖記錄的指標執行3,如果失敗執行2.
  2. 發生自旋,自旋到一定次數,如果修改成功執行3,否則鎖膨脹為重量級鎖。
  3. 執行同步程式碼塊。

解鎖: 輕量級解鎖時,會使用原子的CAS操作將Displaced Mark Word替換回到物件頭,如果成功,則表示沒有競爭發生。如果失敗,表示當前鎖存在競爭,鎖就會膨脹成重量級鎖。

鎖的優缺點

徹底搞懂鎖升級

在這裡插入圖片描述

lock

它是在1.5之後提供的一個獨佔鎖介面,它的實現類是ReentrantLock,相比較synchronized這種隱式鎖(不用手動加鎖和釋放鎖)的便捷性,但是提供了更加鎖的可操作性、可中斷的獲取鎖以及超時獲取鎖等多種synchronized不具備的特性。

使用方法

在finally中釋放鎖,目的保證獲取鎖最終被釋放。不要在獲取鎖寫在try裡,因為如果在獲取鎖時發生了異常,異常丟擲的同時,也會導致鎖無故釋放。

AQS

AQS是佇列同步器(AbstractQueuedSynchronizer),是用來構建鎖或者其他同步器的基礎框架,它使用了一個int成員變量表示同步狀態,通過內建的FIFO佇列來完成資源獲取的執行緒排隊工作問題。AQS在內部維護了一個單一的狀態資訊state,可以通過getState、setState、compareAndSetState(CAS操作)修改此值,對於ReentrantLock來說,state可以用來表示當前執行緒獲取鎖的可重入次數。ReentrantLock中當一個執行緒獲取了鎖,在AQS的內部會進行compareAndSetState將state變為1,如果再次獲取就設定為2,釋放鎖也會去修改state值,只有當值變為0時,其他執行緒才能獲得鎖。

鎖的介紹

AQS底層維護state和佇列來實現獨佔和共享兩種鎖。

**獨佔鎖:**每次只能有一個執行緒能持有鎖,如lock、synchronized。 **共享鎖:**允許多個執行緒同時獲取鎖,併發訪問共享資源,如ReadWriteLock。

lock分為公平鎖和非公平鎖,實現了AQS介面,通過FIFO設定鎖的優先順序。

**公平鎖:**根據執行緒獲取鎖的時間來判斷,等待時間越久的執行緒優先被執行。Lock中初始化的時候ReentrantLock(true),預設為false,效率較低因為需要判斷執行緒的等待時間。

**非公平鎖:**搶佔鎖資源,不能保證獲取鎖的執行緒優先順序,效率較高,因為獲取鎖是競爭的。

兩者不同

  1. synchronized是Java的關鍵字,lock是提供的類。
  2. synchronized提供不需要手動加鎖和釋放的隱式鎖,釋放鎖的條件是程式碼執行完或者丟擲異常自動釋放。lock必須手動加鎖和釋放鎖,另外還提供了可中斷鎖、超時獲取鎖、判斷鎖狀態。
  3. synchronized是可重入、不可中斷、非公平,lock是可重入、可中斷、公平(兩者皆可)
  4. synchronized適合程式碼量少的同步,lock適合程式碼量同步多的。**

Condition介面

還記得在Java併發二中有一道生產者消費者,使用的是synchronized+wait(notify),lock中也提供了這種等待通知型別的方法await和signal,當前執行緒呼叫這些方法時,需要提前獲取到Condition物件關聯的鎖,Condition是依賴於Lock物件,呼叫lock物件中的newCondition。

老樣子還是先定義一個容器:

在這裡插入圖片描述 生產者:啟5個執行緒往容器裡新增資料。

消費者:啟10執行緒消費資料

最後

註釋基本明確,就不多說了。wait和notify是配合synchronized使用,await和signal是配合lock使用,區別在於喚醒時notify不能指定執行緒喚醒,signal可以喚醒具體的執行緒,更小的粒度控制鎖。

閱讀更多

金三銀四,2019最新面試實戰總結

如何通過抓包實戰來學習Web協議?

動畫:一招學會TCP的三次握手和四次揮手

上兩個月,15家面試,幾個offer , 我的面試歷程!

相信自己,沒有做不到的,只有想不到的

在這裡獲得的不僅僅是技術!

相關推薦

Java真的這麼複雜

前言 作者前面也寫了幾篇關於Java併發程式設計,以及執行緒和volatil的基礎知識,有興趣可以閱讀作者的原文部落格,今天關於J

Java記憶體模型FAQ(二) 其他語言像C++記憶體模型

譯者:Alex 大部分其他的語言,像C和C++,都沒有被設計成直接支援多執行緒。這些語言對於發生在編譯器和處理器平臺架構的重排序行為的保護機制會嚴重的依賴於程式中所使用的執行緒庫(例如pthreads),編譯器,以及程式碼所執行的平臺所提供的保障。 原文 Do other languag

Java 字串拼接竟然這麼多姿勢

二哥,我今年大二,看你分享的《阿里巴巴 Java 開發手冊》上有一段內容說:“迴圈體內,拼接字串最好使用 StringBuilder 的 append 方法,而不是 + 號操作符。”到底為什麼啊,我平常一直就用的‘+’號操作符啊!二哥有空的時候能否寫一篇文章分析一下呢? 就在昨天,一位叫小菜的讀者微信我說

兄弟諾基亞情結

學習 國內 商品 隨著 諾基亞 移動互聯 實的 訪問網站 而是 同事新換了一部手機,不是華為、小米,不是OPPO、vivo、不是蘋果三星,而是諾基亞,當時還有點嘲笑同事的諾基亞情結,而同事說了這樣一句話:“這款諾基亞手機賣的很火,一千多元,年銷量近千萬臺!”瞬間震驚。諾基亞

作為一名Android開發者過迷茫

前言經常聽新進的小白問道,Android是不是飽和了?想寫一篇關於Android開發者憂慮的文章很久了,今天才提起勇氣寫。最近不管是在微信、QQ群,還是在各大部落格網站,都隨處聽得到Android開發不景氣的聲音,而現在的大資料、人工智慧的火爆程度,很大程度上對Android開發者不免有一定的影響,寫這篇文章

作為一名Android開發者過迷茫

前言 經常聽新進的小白問道,Android是不是飽和了?想寫一篇關於Android開發者憂慮的文章很久了,今天才提起勇氣寫。最近不管是在微信、QQ群,還是在各大部落格網站,都隨處聽得到Android開發不景氣的聲音,而現在的大資料、人工智慧的火爆程度,很大程度上對Android開發者不免有一定的影響,寫這篇

【科普】泰國試管嬰兒次數限制的?

    不孕症患者的數量一直在增加,大多數不孕症患者一開始並沒有注意這些問題,直到他們長大並發現他們錯過了進行體外受精的最佳時間,並且年齡問題體外受精的成功率有一個與它有很大關係。   當許多女性年輕時,她們總覺得自己應該忙於自己的事業並生孩子。因此,上學的問題總是延遲,等待成熟的時間。當你想要

程式設計師的20種狀態中招

「1」 被老闆委派接手剛剛離職同事的專案...     「2」 當他們要求我測試所有應用功能時     「3」 準備下班的時候,測試又提bug過來了…      「

小小的一個聖誕樹燈原來這麼大的學問?

聖誕樹據說大約在16世紀誕生的,是德國人最先把常青的松柏枝拿到屋中去擺設。後來,德國傳教士馬丁.路德把蠟燭放在樹林中的樅樹枝上,然後點燃,使它看起來像是引導人們到伯利恆的星光,如同2000年前的東方三博士依照天上的星星找到耶穌一般。 這是聖誕樹的由來,但今天的聖誕樹早已用其他材質做的假樹代替了松

人工智慧和大資料什麼相似之處和不同之處之間什麼共同點?

    大資料vs.人工智慧是一種公平的比較嗎?在某種程度上,它是,但首先讓我們先釐清它們之間的區別。     人工智慧和大資料是人們耳熟能詳的流行術語,但也可能會有一些混淆。人工智慧和大資料有什麼相似之處和不同之處?它們有什麼共同點嗎?它們是否相似?

酒鏈世界聽說過

酒鏈世界區塊鏈軟體開發的產品闡明資訊或許還不夠細緻和全面,如果您需求更詳細瞭解酒鏈世界區塊鏈軟體開發的相關資訊或討取相關材料,歡迎隨時與我聯絡![陳生134-1709-1886] 酒鏈世界形式區塊鏈開發技能,酒鏈世界app區塊鏈定製開發,酒鏈世界區塊鏈挖礦系統開發   區塊鏈的誕生,帶來了解決這個問題的曙

成為一個程式設計師那麼難

寫在開頭 工作有一年多了,從來沒有寫過部落格,以前總是拿個本子記一下。本子寫完了後就扔到垃圾桶裡面去了。忽然發現原來自己學習的很多東西都忘記了,需要用的時候找都找不到。更加沒有留下什麼學習心得,經驗之談。 這是第二家公司,在這裡已經工作一年零兩個月。從一個語言

那麼多人選擇“人工智慧”真的那麼好?

今天冪次妹講的是4個關於“人工智慧”的故事,來看看那麼多人為什麼選擇“人工智慧”。冪次妹在這裡不

Go真的這麼

為什麼要學習Go Go是未來的服務端語言— Tobias Lütke, Shopify。在過去的幾年中,Golang逐步流行起來。 還有什麼能比一門新語言讓碼農們瘋狂呢? 因此,我開始學習了一段時間Golang,在這裡我將告訴你為什麼你也應該學習這種新語言。 在本文裡我

Ubuntu真的那麼好

以前曾經用過一陣子Redhat7.2, Redhat9, SuSE9, SuSE10 desktop。都覺得還湊乎。想裝什麼東西,基本上也都能搞定。可是試用一下Ubuntu8.10,就把人搞得有點糊塗了。配置那個該死的網路,配了半天,能上,重新啟動以後,又不以聯了,得重新啟動

為什麼那麼多人選擇“人工智慧”真的那麼好

今天想要講的是4個關於“人工智慧”的故事,來看看那麼多人為什麼選擇“人工智慧”。我在這裡不會告訴

用Python分分鐘爬取豆瓣本週口碑榜就是這麼秀!

平常在生活中,不知道大家是怎麼找電影的,反正小編是通過電影本週口碑榜來找的,個人感覺通過這種方式找來的電影都挺不錯的。既然提到口碑榜,不如我們來爬下豆瓣電影本週口碑榜上的電影吧,怎麼爬嘞,當然是用我們的Python爬蟲啦!下面開始簡單的介紹如何寫爬蟲。   在寫爬蟲前,我們首先簡單

現在學生物出路那麼不濟

作者:匿名作者 喝茶歸來,更新如下: 在2018年,清華大學成為了全國排名第一的世界一流大學,人們對清華二字的恐懼根深蒂固,不敢談論他,甚至不敢說出他的名字,於是以“神祕大學”稱之。 利益相關:神祕大學生命科學學院201X屆畢業生,已轉行,勸退黨徒,匿名。如果

==和equals()都可用於比較兩個運算元是否相等它們什麼區別

= =是一個關係運算符,用於判斷兩個簡單變數的值是否相等,或兩個引用變數的引用地址是否相等。 equals()是一個方法,用於判斷引用變數引用地址指向的儲存內容是否相等。 equals()是Object類中定義的一個方法,由於其他引用型別預設繼承Object,因此該方法在其他引用型別中都可以

網易雲躋身直播行列別樣天地

一對一直播原始碼開發技術難點很多,由於直播行業的高速發展,使得一對一直播原始碼開發的程序也隨之加快,生怕會掉隊。網易雲自上線以來,因為工作人員的細心、善良引起了廣大使用者的使用,眾所周知網易雲是一個專於做音樂的APP平臺,近來,網易雲又上線了一款音樂直播平臺——look直播。直播改變了遊戲宣發渠道,是否可以改