鎖是java併發編程中最重要的同步機制。鎖除了讓臨界區互斥執行外,還可讓釋放鎖的線程向獲取同一個鎖的線程發送消息。html
當線程釋放鎖時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存中;
當線程獲取鎖時,JMM會當前線程擁有的本地內存共享變量置爲無效,從而使得被監視器保護的臨界區代碼必需要從主內存中去讀取共享變量;java
CAS是單詞compare and set的縮寫,意思是指在set以前先比較該值有沒有變化,只有在沒變的狀況下才對其賦值。編程
問題:如何在沒有鎖的狀況下實現i++原子操做?緩存
CAS操做涉及到三個操做數,一個是內存值,一個是舊的預期值,一個是更新後的值,若是內存值和舊的預期值沒有發生變化,才設置成新的值。多線程
public final int incrementAndGet() { for (;;) { //獲得預期值 int current = get(); //獲得更新後的值 int next = current + 1; //經過CAS操做驗證是否發生變化 if (compareAndSet(current, next)) return next; } }
CAS的原子性其實是CPU實現的.併發
CAS操做用途:能夠用CAS在無鎖的狀況下實現原子操做,但要明確應用場合,很是簡單的操做且又不想引入鎖能夠考慮使用CAS操做,當想要非阻塞地完成某一操做也能夠考慮CAS。不推薦在複雜操做中引入CAS,會使程序可讀性變差,且難以測試,同時會出現ABA問題。性能
2.1 volatile關鍵字的特性:測試
(1)可見性:對一個volatile關鍵字的讀,老是能看到(任意線程)對這個關鍵字的寫 (2)原子性:對任意單個volatile變量的寫操做,具備原子性(注:多個volatile組合操做不具備原子性)
2.2 內存語義優化
2.3 實現原理操作系統
instance = new Singleton(); 定義一個volatile變量
其對應編譯後的cpu指令爲:
0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);
由編譯後的彙編指令能夠看出,改指令相比其餘指令多個一個lock前綴
Lock前綴的指令在多核處理器下會引起了兩件事情:
具體實現細節:
鎖的內存語義的實現與可重入鎖相關,能夠簡要總結鎖的內存語義的實現包括如下兩種方式:
synchronized也稱爲監視器鎖,由JVM控制實現,每一個對象都有相似監視器同樣的鎖,當監視器鎖被佔用是對象將會處於鎖定狀態, 每一個對象有一個監視器鎖(monitor)。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的全部權,過程以下:
當線程執行monitorexit指令時候,過程以下:
執行monitorexit的線程必須是objectref所對應的monitor的全部者。
指令執行時,monitor的進入數減1,若是減1後進入數爲0,那線程退出monitor,再也不是這個monitor的全部者。其餘被這個monitor阻塞的線程能夠嘗試去獲取這個 monitor 的全部權。
經過這兩段描述,咱們應該能很清楚的看出Synchronized的實現原理,Synchronized的語義底層是經過一個monitor的對象來完成,其實wait/notify等方法也依賴於monitor對象,這就是爲何只有在同步的塊或者方法中才能調用wait/notify等方法,不然會拋出java.lang.IllegalMonitorStateException的異常的緣由。
加輕量鎖的過程很簡單:在當前線程的棧幀(stack frame)中生成一個鎖記錄(lock record),這個鎖記錄比前面說的那個對象鎖(管理線程隊列的monitor)簡單多了,它只是對象頭的一個拷貝。而後把對象頭裏的tag改爲00,並把這個棧幀裏的lock record地址放入對象頭裏。若操做成功,那就完成了輕量鎖操做。若是不成功,說明有線程在競爭,則須要在當前對象上生成重量鎖來進行多線程同步,而後將Tag狀態改成10,並生成Monitor對象(重量鎖對象),對象頭裏也會放入Monitor對象的地址。最後將當前線程t排隊隊列中。
輕量鎖的解鎖過程也很簡單就是把棧幀裏剛纔的那個lock record拷貝到對象頭裏,若替換成功,則解鎖完成,若替換不成功,表示在當前線程持有鎖的這段時間內,其餘線程也競爭過鎖,而且發生了鎖升級爲重量鎖,這時須要去Monitor的等待隊列中喚醒一個線程去從新競爭鎖。
1. 二者都是可重入的鎖; 2. synchronized就不是可中斷鎖,而Lock是可中斷鎖; 3. synchronized是非公平鎖,而lock提供公平鎖的實現; 4. Lock提供讀寫兩種鎖操做;
性能比較:
在JDK1.5中,synchronized的性能是比較低的,線程阻塞和喚醒由操做系統內核完成,頻繁的加鎖和放鎖致使頻繁的上下文切換,形成效率低下;所以在多線程環境下,synchronized的吞吐量降低的很是嚴重。但在JDK1.6時對synchronized進行了不少優化,包括偏向鎖、自適應自旋、輕量級鎖等措施。
當須要如下高級特性時,才應該使用Lock:可定時的、可輪詢的與可中斷的鎖獲取操做,公平隊列,或者非塊結構的鎖。不然,請使用synchronized。
https://www.cnblogs.com/pony1223/p/9428248.html