一、為什么要用鎖,?鎖-是為了解決并發(fā)操作引起的臟讀,、數(shù)據(jù)不一致的問題。 二,、鎖實現(xiàn)的基本原理2.1,、volatileJava編程語言允許線程訪問共享變量, 為了確保共享變量能被準確和一致地更新,,線程應(yīng)該確保通過排他鎖單獨獲得這個變量,。Java語言提供了volatile,在某些情況下比鎖要更加方便,。 volatile在多處理器開發(fā)中保證了共享變量的“ 可見性”,。可見性的意思是當(dāng)一個線程修改一個共享變量時,,另外一個線程能讀到這個修改的值,。 結(jié)論:如果volatile變量修飾符使用恰當(dāng)?shù)脑挘萻ynchronized的使用和執(zhí)行成本更低,,因為它不會引起線程上下文的切換和調(diào)度,。 2.2、synchronizedsynchronized通過鎖機制實現(xiàn)同步,。 先來看下利用synchronized實現(xiàn)同步的基礎(chǔ):Java中的每一個對象都可以作為鎖,。 具體表現(xiàn)為以下3種形式。
當(dāng)一個線程試圖訪問同步代碼塊時,,它首先必須得到鎖,,退出或拋出異常時必須釋放鎖。 2.2.1 synchronized實現(xiàn)原理synchronized是基于Monitor來實現(xiàn)同步的,。 Monitor從兩個方面來支持線程之間的同步:
1,、Java 使用對象鎖 ( 使用 synchronized 獲得對象鎖 ) 保證工作在共享的數(shù)據(jù)集上的線程互斥執(zhí)行。 2,、使用 notify/notifyAll/wait 方法來協(xié)同不同線程之間的工作,。 3、Class和Object都關(guān)聯(lián)了一個Monitor,。 Monitor 的工作機理
參考文檔:https://www.ibm.com/developerworks/cn/java/j-lo-synchronized 2.2.2 synchronized具體實現(xiàn)1、同步代碼塊采用monitorenter,、monitorexit指令顯式的實現(xiàn),。 2、同步方法則使用ACC_SYNCHRONIZED標記符隱式的實現(xiàn),。 通過實例來看看具體實現(xiàn): javap編譯后的字節(jié)碼如下: monitorenter 每一個對象都有一個monitor,,一個monitor只能被一個線程擁有。當(dāng)一個線程執(zhí)行到monitorenter指令時會嘗試獲取相應(yīng)對象的monitor,,獲取規(guī)則如下:
monitorexit 只有擁有相應(yīng)對象的monitor的線程才能執(zhí)行monitorexit指令,。每執(zhí)行一次該指令monitor進入數(shù)減1,當(dāng)進入數(shù)為0時當(dāng)前線程釋放monitor,,此時其他阻塞的線程將可以嘗試獲取該monitor,。 2.2.3 鎖存放的位置鎖標記存放在Java對象頭的Mark Word中。 Java對象頭長度 32位JVM Mark Word 結(jié)構(gòu) 32位JVM Mark Word 狀態(tài)變化 64位JVM Mark Word 結(jié)構(gòu) 2.2.3 synchronized的鎖優(yōu)化JavaSE1.6為了減少獲得鎖和釋放鎖帶來的性能消耗,,引入了“偏向鎖”和“輕量級鎖”,。 在JavaSE1.6中,鎖一共有4種狀態(tài),,級別從低到高依次是:無鎖狀態(tài),、偏向鎖狀態(tài)、輕量級鎖狀態(tài)和重量級鎖狀態(tài),這幾個狀態(tài)會隨著競爭情況逐漸升級,。 鎖可以升級但不能降級,,意味著偏向鎖升級成輕量級鎖后不能降級成偏向鎖。這種鎖升級卻不能降級的策略,,目的是為了提高獲得鎖和釋放鎖的效率,。 偏向鎖:無鎖競爭的情況下為了減少鎖競爭的資源開銷,引入偏向鎖,。 輕量級鎖:輕量級鎖所適應(yīng)的場景是線程交替執(zhí)行同步塊的情況。 鎖粗化(Lock Coarsening):也就是減少不必要的緊連在一起的unlock,,lock操作,,將多個連續(xù)的鎖擴展成一個范圍更大的鎖。 鎖消除(Lock Elimination):鎖削除是指虛擬機即時編譯器在運行時,,對一些代碼上要求同步,,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進行削除。 適應(yīng)性自旋(Adaptive Spinning):自適應(yīng)意味著自旋的時間不再固定了,,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定,。如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖,,并且持有鎖的線程正在運行中,,那么虛擬機就會認為這次自旋也很有可能再次成功,進而它將允許自旋等待持續(xù)相對更長的時間,,比如100個循環(huán),。另一方面,如果對于某個鎖,,自旋很少成功獲得過,,那在以后要獲取這個鎖時將可能省略掉自旋過程,以避免浪費處理器資源,。 2.2.4 鎖的優(yōu)缺點對比2.3,、CASCAS,在Java并發(fā)應(yīng)用中通常指CompareAndSwap或CompareAndSet,,即比較并交換,。 1、CAS是一個原子操作,,它比較一個內(nèi)存位置的值并且只有相等時修改這個內(nèi)存位置的值為新的值,,保證了新的值總是基于最新的信息計算的,如果有其他線程在這期間修改了這個值則CAS失敗,。CAS返回是否成功或者內(nèi)存位置原來的值用于判斷是否CAS成功,。 2、JVM中的CAS操作是利用了處理器提供的CMPXCHG指令實現(xiàn)的。 優(yōu)點:
缺點:
三,、Java中的鎖實現(xiàn)3.1、隊列同步器(AQS)隊列同步器AbstractQueuedSynchronizer(以下簡稱同步器),,是用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,。 3.1.1、它使用了一個int成員變量表示同步狀態(tài),。3.1.2,、通過內(nèi)置的FIFO雙向隊列來完成獲取鎖線程的排隊工作。
3.1.3、獨占式/共享式鎖獲取獨占式:有且只有一個線程能獲取到鎖,,如:ReentrantLock,; 共享式:可以多個線程同時獲取到鎖,如:CountDownLatch,; 獨占式
共享式:
四,、鎖的使用用例4.1、ConcurrentHashMap的實現(xiàn)原理及使用ConcurrentHashMap類圖 ConcurrentHashMap數(shù)據(jù)結(jié)構(gòu) 結(jié)論:ConcurrentHashMap使用的鎖分段技術(shù),。首先將數(shù)據(jù)分成一段一段地存儲,,然后給每一段數(shù)據(jù)配一把鎖,當(dāng)一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候,,其他段的數(shù)據(jù)也能被其他線程訪問,。 作者:景小財 鏈接:https://www.jianshu.com/p/e674ee68fd3f |
|