一、鎖狀態
鎖的狀態只能升級不能降級。java
沒有鎖對資源進行鎖定,全部線程都能訪問並修改同一個資源,但同時只有一個線程能修改爲功。其餘修改失敗的線程會不斷重試,直到修改爲功,如CAS原理和應用是無鎖的實現。算法
偏向鎖是指一段同步代碼一直被一個線程訪問,那個該線程會自動獲取鎖,下降獲取鎖的代價。多線程
是指當鎖是偏向鎖的時候,被另外的線程所訪問,偏向鎖就會升級爲輕量級鎖,其餘線程會經過自旋的形式嘗試獲取鎖,不會阻塞,從而提升性能。經過cas操做和自旋來解決加鎖問題,自旋超過必定的次數或者已經有一個線程在自旋,又來一個線程獲取鎖時,輕量級鎖會升級爲重量級鎖。性能
升級爲重量級鎖,等待鎖的線程都會進入阻塞狀態。優化
二、樂觀鎖與悲觀鎖
- 樂觀鎖,每次拿數據的時候認爲別人都不會修改,在更新的時候再判斷在此期間有沒有更新數據,可使用版本號等機制,適合讀取多場景,提升性能。
- 悲觀鎖,每次拿數據都認爲別人會修改,都會上鎖,可使用synchronized、獨佔鎖Lock、讀寫鎖等機制,適合寫多的場景,保證寫入操做正確。
三、自旋鎖與適應性自旋鎖
- 自旋鎖:指當一個線程在獲取鎖的時候,若是鎖已經被其餘線程獲取,那麼該線程將循環等待,而後不斷判斷鎖是否能獲取成功,直到獲取到鎖才退出循環。
優勢:線程不進行上下文切換,減小了上下文切換的時間。
存在的問題:若是線程持有鎖的時間較長,其餘線程進入循環,消耗cpu。spa
- 自適應自旋鎖:指的是自旋的時間不固定,由前一個在同一個鎖上自旋的時間和鎖擁有者的狀態來決定。若是在同一個對象上,剛剛經過自旋成功獲取過鎖,且持有鎖的線程正在運行中,那麼虛擬機就會認爲此次自旋頗有可能再次成功。反之自旋操做不多成功獲取鎖,那麼後面獲取這個鎖可能直接省略掉自旋的過程,直接阻塞線程。
四、公平鎖與非公平鎖
- 公平鎖是指多個線程按照申請鎖的順序直接進入隊列排隊,隊列中的第一個線程才能獲取鎖。
- 非公平鎖是指線程先嚐試獲取鎖,獲取不到進入隊列中排隊,若是能獲取到,則無需阻塞直接獲取鎖。
五、重入鎖與非重入鎖
重入鎖:同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖,前提是鎖對象是相同的。操作系統
六、共享鎖與排他鎖
- 共享鎖是指一個鎖能夠被多個線程鎖持有。
- 排它鎖或者叫獨享鎖或者互斥鎖 指鎖一次只能被一個線程所持有。
七、讀寫鎖
- 讀鎖是共享的,寫鎖是獨佔的。
- 讀讀之間不會互斥,讀寫互斥,寫寫互斥,讀寫鎖提升了讀的性能。
八、CAS
CompareAndSwap比較與交換,是一種無鎖算法,原子類使用了CAS實現了樂觀鎖。
帶來的問題:線程
- ABA問題
解決思路在變量前面加版本號,每次變量更新的時候都將版本號+1,每次更新的時候要求版本>=當前版本(AtomicStampedReference)指針
- 循環時間長開銷大,CAS操做若是長時間執行不成功,會致使其一直自旋,cpu消耗大。
- 只能保證一個共享變量的原子操做。
能夠把多個變量放在一個對象裏面進行CAS操做。code
九、鎖優化
9.一、鎖升級
- 偏向鎖的升級
線程A獲取鎖對象時,會在java對象頭和棧幀中記錄偏向的線程A的id,線程A再次獲取鎖時,只須要比較java頭中的線程id與當前Id是否相等,若是一致則無需經過cas加鎖解鎖。若是不一致,說明有線程B來獲取鎖,那麼要判斷java頭中偏向鎖的線程是否存活,若是沒有存活,鎖對象被置爲無鎖狀態,線程B可將鎖對象置爲B的偏向鎖。若是存活,則查看A是否還須要繼續持有對當前鎖,若是不須要持有,則將鎖置爲無鎖狀態,偏向新的線程,若是還繼續持有鎖對象,則暫停A線程,撤銷偏向鎖,將鎖升級爲輕量級鎖。
- 輕量級鎖的升級
線程A獲取輕量級鎖時會把鎖的對象頭複製到本身的線程棧針中,而後經過cas把對象頭中的內容替換爲A所記錄的地址。此時線程B也想獲取鎖,發現A已經獲取鎖,那麼線程B就自旋等待。等到自旋次數到了或者線程A正在執行,線程B自旋等待,此時來了線程C來競爭鎖對象,這個時候輕量級鎖就會膨脹爲重量級鎖。重量級鎖會把未得到到鎖對象的線程所有變爲阻塞狀態該,防止cpu空轉。
9.二、鎖粗化
將多個連續的加鎖,解鎖操做鏈接在一塊兒,擴展成爲一個範圍更大的鎖,避免頻繁的加解鎖操做。
9.三、鎖消除
經過逃逸分析,去除不可能存在共享資源競爭的鎖,經過這種方式消除沒有必要的鎖。
十、synchronized底層實現
- synchronized經過Monitor實現同步,Monitor依賴於底層操做系統互斥鎖來實現線程同步。
- java對象頭是由markword(標記字段)和klass point(類型指針)組成。markword存儲對象的hashcode,分代年齡和鎖標誌位信息。Klass point 指向對象元數據的指針,虛擬機經過這個指針來肯定對象是哪一個類的實例。
- synchronized修飾同步代碼塊,是使用monitorenter和monitorexit來控制的,經過java對象頭中的鎖計數器。
- 修飾方法時會將方法標識爲ACCSYNCHRONIZE,JVM經過這個標誌來判斷方法是否是同步方法。
十一、synchronized與ReentrantLock的區別
- 二者都是悲觀鎖,可重入鎖。
- ReentrantLock 可中斷,能夠實現公平鎖,能夠綁定多個條件。
- ReentrantLock須要顯示的調用鎖和釋放鎖,synchronized屬於java關鍵字,不須要顯式的釋放。
十二、volatile關鍵字
- 保證變量內存可見。
- 禁止指令重排序。
volatile和synchronized的區別:
- volatile不會阻塞,synchronized會阻塞。
- volatile保證數據的內存可見性但不能保證原子性,synchronized二者都能保證。
- volatile主要解決變量在線程之間的可見性,而synchronized主要解決多線程訪問資源的同步性。
1三、Atomic原子類實現
使用cas操做 + volatile + native方法保證同步。
1四、AQS
AQS(AbstractQueuedSynchronizer)內部維護的是一個FIFO的雙向同步隊列,若是當前線程競爭鎖失敗,AQS會把當前線程以及等待狀態信息構形成一個Node加入到同步隊列中,同時在阻塞該線程。當獲取鎖的線程釋放鎖之後,會從隊列中喚醒一個阻塞的節點線程。使用內部的一個state來控制是否獲取鎖,當state=0時表示無鎖狀態,state>0時表示已經有線程獲取了鎖。
1五、AQS的組件
- semaphore 可指定多個線程同時訪問某個共享資源。
- countDownLatch 一個線程A等待其餘線程執行完成以後才繼續執行。
- cyclicBarrier 一組線程等待至某個狀態以後同時執行。
countDownLatch和CyclicBarrier的區別
- countDownLatch是一個線程等一組線程執行完成以後才執行, cyclicBarrier是一組線程互相等待至某個狀態以後,同時執行。
- countDownLatch不能複用,cyclicBarrier能夠重用。
1六、鎖降級
鎖降級是指將寫鎖降級爲讀鎖,這個過程就是當前線程已經獲取到寫鎖的時候,再獲取到讀鎖,隨後釋放寫鎖的過程,這麼作的目的爲的就是保證數據的可見性。
1七、逃逸分析
- 逃逸分析就是分析對象的動態做用域,當一個對象在方法中被定義後,他可能被外部方法所引用,做爲參數傳遞到其餘方法中,成爲方法逃逸,賦值給類變量或者能夠被其餘線程訪問的實例變量成爲線程逃逸。
- 使用逃逸分析,編譯器能夠對代碼作優化。好比:同步省略(鎖消除),將堆分配轉化爲棧分配,標量替換。
- 使用逃逸分析的缺點,無法保證逃逸分析的性能必定高於其餘性能。極端的話通過逃逸分析後,全部的對象都逃逸了,那麼逃逸分析的過程就浪費了。