備註:整理一些同步技術,方便往後回顧。目前技術還在學習中,瞭解到同步方面的新知識會補充到本文。java
synchronized
一、解讀:程序員
(1)jvm層面的同步技術,字節碼實現。當代碼執行出現問題的時候(好比說拋出異常),JVM會自動釋放鎖,讓其餘阻塞的線程繼續執行 (2)可重入的:當線程已經獲取鎖對象,而且再次進入同步塊,把鎖的計數器+1,當執行monitorexit時,把鎖的計數器-1,當計數器爲0爲止,對象被釋放
二、使用狀況:redis
(1)實例對象做爲鎖對象:對象實例可能有多個,所以若是不能保證對象單例的狀況下,同步可能會出現問題。 (2)類對象做爲鎖對象:每一個類只有1個類對象。 (3)靜態**同步方法:使用實例對象做爲鎖 (4)成員同步方法:使用類對象做爲鎖
三、注意事項:算法
(1)儘可能減小同步的範圍,提升程序併發性,減小死鎖的可能性 (2)使用Class類對象和實例對象的區別 (3)靜態方法和非靜態方法進行同步的區別
Lock接口
一、解讀:安全
(1)Lock接口的同步更爲靈活,使用起來也複雜一些 (2)能夠設置等待時間,在線程請求鎖的時候,若是超過等待時間,則線程中止等待。 (4)Lock可以響應中斷,讓等待狀態的線程中止等待。 (5)API提供返回值,知道線程是否成功獲取鎖 (6)經常使用的實現類:ReentrantLock
二、使用狀況:線程須要支持中斷、查看線程是否成功得到鎖
三、注意事項:多線程
(1)代碼層面實現的鎖機制,遇到異常狀況,JVM不會自動釋放鎖。使用lock(),必須使用unlokc()
ReentrantLock
一、解讀:併發
(1)使用AQS框架實現、實現了Lock接口 (2)排他鎖,只有一個線程能獲取並使用資源 (3)構造方法提供一個可選的公平參數,公平與非公平有何區別,所謂公平就是嚴格按照FIFO的順序獲取鎖,非公平全按程序員本身設計規則來獲取鎖,好比能夠根據優先級,也能夠按照運行次數等規則來選擇
(4)能夠綁定多個Condition對象(Condition對象指的是,當線程得到鎖,進入同步塊之後,還須要知足Condition對象的條件,不然仍然會掛起等待,直到其餘線程喚醒)框架
CountDownLatch
一、解讀:jvm
(1)使用AQS框架實現 (2)共享鎖,能夠有多個線程同時持有該鎖 (3)任務分紅N個任務執行,state字段初始化爲N。開始執行任務,調用線程調用await()函數掛起。每一個子線程執行完畢之後,執行countDown()函數,state字段減1,直到字段變爲0。調用線程從CountDown()函數返回,繼續後續動做。
二、使用狀況:
三、注意事項:分佈式
wait()、Notify()
一、解讀:
(1)調用對象的wait()方法,會將持有該對象的線程掛起,直到有別的線程調用這個對象的notify()方法
join()
一、解讀:
(1)在B線程中調用A線程的Join方法,則B會等待A線程執行完畢,再往下執行
cas算法
一、解讀:
(1)使用重試的方式,樂觀鎖的一種實現
二、使用場景:
(1)AtomicInteger
Semaphore
一、解讀:
(1)利用AQS實現。 (2)共享鎖,能夠有多個線程同時持有該鎖 (3)內部維護一個「許可集」,其實也就是state字段,標明同時最多有state個線程能夠獲取鎖(成功調用acquire()函數)
CyclicBarrier
一、解讀:
(1)內存屏障。設立一個目標,當全部線程都達到這個目標時,程序才能往下執行。
volatile
一、解讀:
(1)volatile修飾的變量對全部線程具備可見性。一個線程修改了這個變量的值,其餘線程對於這個新值是能夠當即得知的。緣由以下:對於共享變量,當一個線程修改了這個變量,其餘線程若是要讀取該變量,只能從主內存中讀取,而不能讀取工做內存中的變量,這樣就保證了線程讀取的變量都是最新的 (2)volatile修飾的變量禁止 指令重排序 的優化(普通變量不能保證變量賦值操做的順序與代碼中同樣,因爲存在指令重排序優化,java代碼變成彙編代碼以後,代碼執行順序可能發生變化) (3)volatile沒法保證原子性。 (4)除了volatile關鍵字,還有兩個關鍵字也能實現可見性。 A、 synchronize:「在對變量進行unlock操做以前,先把工做內存裏面的值同步到主內存」,由這條規則得到可見性。 B、 final:final修飾的字段一旦被初始化完成,其餘線程中就能看到final字段的值
二、使用狀況:
(1)某些狀況下能夠避免加鎖,提升程序併發性
redis實現分佈式鎖
一、解讀:
(1)Redis是單線程模型,單個命令操做是原子性的。 (2)NX指令: a、NX表示若是key不存在就添加,存在則直接返回 b、NX是實現分佈式鎖的關鍵。 c、實例:SET key uuid NX PX timeout SET resource_name uniqueVal NX PX 30000
二、注意點:
(1)爲了防止由於各類緣由致使沒法釋放鎖,因此會利用expire key second
把key設置一個過時時間
(2)利用expire設置過時時間,仍是可能存在問題。好比說在setnx key value
和expire key second
之間,發生了系統故障(或者其餘緣由),致使key沒有設置過時時間,仍是可能發生死鎖。這時候,能夠利用一條指令set lock:codehole true ex 5 nx
,這條指令至關於先執行setnx再執行expire,同時保證原子性。
(3)在加鎖和釋放鎖時,須要保證是同一把鎖,一般的方法是使用uuid,存入key中,在釋放鎖時,把uuid和key中的值對比,相同則是同一把鎖。
爲何須要保證同一把鎖呢? 假設有A、B、C三臺機器,A先獲取鎖,假設A的業務執行時間比較長,鎖過時了(A的鎖被刪除了),這是B再請求而且獲取鎖,若是這時候A執行完了,不用判斷是否同一把鎖的狀況下,A就刪除了B的鎖。這時候,若是C再請求獲取鎖,就出現B、C同時執行業務代碼的狀況。
Zookeeper實現分佈式鎖