面試【JAVA基礎】鎖


一、鎖狀態

鎖的狀態只能升級不能降級。java

  • 無鎖 沒有鎖對資源進行鎖定,全部線程都能訪問並修改同一個資源,但同時只有一個線程能修改爲功。其餘修改失敗的線程會不斷重試,直到修改爲功,如CAS原理和應用是無鎖的實現。
  • 偏向鎖 偏向鎖是指一段同步代碼一直被一個線程訪問,那個該線程會自動獲取鎖,下降獲取鎖的代價。
  • 輕量級鎖 是指當鎖是偏向鎖的時候,被另外的線程所訪問,偏向鎖就會升級爲輕量級鎖,其餘線程會經過自旋的形式嘗試獲取鎖,不會阻塞,從而提升性能。經過cas操做和自旋來解決加鎖問題,自旋超過必定的次數或者已經有一個線程在自旋,又來一個線程獲取鎖時,輕量級鎖會升級爲重量級鎖。
  • 重量級鎖 升級爲重量級鎖,等待鎖的線程都會進入阻塞狀態。

二、樂觀鎖與悲觀鎖

  1. 樂觀鎖,每次拿數據的時候認爲別人都不會修改,在更新的時候再判斷在此期間有沒有更新數據,可使用版本號等機制,適合讀取多場景,提升性能。
  2. 悲觀鎖,每次拿數據都認爲別人會修改,都會上鎖,可使用synchronized、獨佔鎖Lock、讀寫鎖等機制,適合寫多的場景,保證寫入操做正確。

三、自旋鎖與適應性自旋鎖

  • 自旋鎖:指當一個線程在獲取鎖的時候,若是鎖已經被其餘線程獲取,那麼該線程將循環等待,而後不斷判斷鎖是否能獲取成功,直到獲取到鎖才退出循環。 優勢:線程不進行上下文切換,減小了上下文切換的時間。 存在的問題:若是線程持有鎖的時間較長,其餘線程進入循環,消耗cpu。
  • 自適應自旋鎖:指的是自旋的時間不固定,由前一個在同一個鎖上自旋的時間和鎖擁有者的狀態來決定。若是在同一個對象上,剛剛經過自旋成功獲取過鎖,且持有鎖的線程正在運行中,那麼虛擬機就會認爲此次自旋頗有可能再次成功。反之自旋操做不多成功獲取鎖,那麼後面獲取這個鎖可能直接省略掉自旋的過程,直接阻塞線程。

四、公平鎖與非公平鎖

  1. 公平鎖是指多個線程按照申請鎖的順序直接進入隊列排隊,隊列中的第一個線程才能獲取鎖。
  2. 非公平鎖是指線程先嚐試獲取鎖,獲取不到進入隊列中排隊,若是能獲取到,則無需阻塞直接獲取鎖。

五、重入鎖與非重入鎖

重入鎖:同一個線程在外層方法獲取鎖的時候,在進入內層方法會自動獲取鎖,前提是鎖對象是相同的。算法

六、共享鎖與排他鎖

  1. 共享鎖是指一個鎖能夠被多個線程鎖持有。
  2. 排它鎖或者叫獨享鎖或者互斥鎖 指鎖一次只能被一個線程所持有。

七、讀寫鎖

  1. 讀鎖是共享的,寫鎖是獨佔的。
  2. 讀讀之間不會互斥,讀寫互斥,寫寫互斥,讀寫鎖提升了讀的性能。

八、CAS

CompareAndSwap比較與交換,是一種無鎖算法,原子類使用了CAS實現了樂觀鎖。 帶來的問題:多線程

  1. ABA問題 解決思路在變量前面加版本號,每次變量更新的時候都將版本號 1,每次更新的時候要求版本>=當前版本(AtomicStampedReference)性能

  2. 循環時間長開銷大,CAS操做若是長時間執行不成功,會致使其一直自旋,cpu消耗大。優化

  3. 只能保證一個共享變量的原子操做。 能夠把多個變量放在一個對象裏面進行CAS操做。操作系統

九、鎖優化

9.一、鎖升級
  1. 偏向鎖的升級 線程A獲取鎖對象時,會在java對象頭和棧幀中記錄偏向的線程A的id,線程A再次獲取鎖時,只須要比較java頭中的線程id與當前Id是否相等,若是一致則無需經過cas加鎖解鎖。若是不一致,說明有線程B來獲取鎖,那麼要判斷java頭中偏向鎖的線程是否存活,若是沒有存活,鎖對象被置爲無鎖狀態,線程B可將鎖對象置爲B的偏向鎖。若是存活,則查看A是否還須要繼續持有對當前鎖,若是不須要持有,則將鎖置爲無鎖狀態,偏向新的線程,若是還繼續持有鎖對象,則暫停A線程,撤銷偏向鎖,將鎖升級爲輕量級鎖。
  2. 輕量級鎖的升級 線程A獲取輕量級鎖時會把鎖的對象頭複製到本身的線程棧針中,而後經過cas把對象頭中的內容替換爲A所記錄的地址。此時線程B也想獲取鎖,發現A已經獲取鎖,那麼線程B就自旋等待。等到自旋次數到了或者線程A正在執行,線程B自旋等待,此時來了線程C來競爭鎖對象,這個時候輕量級鎖就會膨脹爲重量級鎖。重量級鎖會把未得到到鎖對象的線程所有變爲阻塞狀態該,防止cpu空轉。
9.二、鎖粗化

將多個連續的加鎖,解鎖操做鏈接在一塊兒,擴展成爲一個範圍更大的鎖,避免頻繁的加解鎖操做。線程

9.三、鎖消除

經過逃逸分析,去除不可能存在共享資源競爭的鎖,經過這種方式消除沒有必要的鎖。3d

十、synchronized底層實現

  • synchronized經過Monitor實現同步,Monitor依賴於底層操做系統互斥鎖來實現線程同步。
  • java對象頭是由markword(標記字段)和klass point(類型指針)組成。markword存儲對象的hashcode,分代年齡和鎖標誌位信息。Klass point 指向對象元數據的指針,虛擬機經過這個指針來肯定對象是哪一個類的實例。
  • synchronized修飾同步代碼塊,是使用monitorenter和monitorexit來控制的,經過java對象頭中的鎖計數器。
  • 修飾方法時會將方法標識爲ACCSYNCHRONIZE,JVM經過這個標誌來判斷方法是否是同步方法。

十一、synchronized與ReentrantLock的區別

  1. 二者都是悲觀鎖,可重入鎖。
  2. ReentrantLock 可中斷,能夠實現公平鎖,能夠綁定多個條件。
  3. ReentrantLock須要顯示的調用鎖和釋放鎖,synchronized屬於java關鍵字,不須要顯式的釋放。

十二、volatile關鍵字

  1. 保證變量內存可見。
  2. 禁止指令重排序。

volatile和synchronized的區別:指針

  • volatile不會阻塞,synchronized會阻塞。
  • volatile保證數據的內存可見性但不能保證原子性,synchronized二者都能保證。
  • volatile主要解決變量在線程之間的可見性,而synchronized主要解決多線程訪問資源的同步性。

1三、Atomic原子類實現

使用cas操做 volatile native方法保證同步。code

1四、AQS

AQS(AbstractQueuedSynchronizer)內部維護的是一個FIFO的雙向同步隊列,若是當前線程競爭鎖失敗,AQS會把當前線程以及等待狀態信息構形成一個Node加入到同步隊列中,同時在阻塞該線程。當獲取鎖的線程釋放鎖之後,會從隊列中喚醒一個阻塞的節點線程。使用內部的一個state來控制是否獲取鎖,當state=0時表示無鎖狀態,state>0時表示已經有線程獲取了鎖。

1五、AQS的組件

  1. semaphore 可指定多個線程同時訪問某個共享資源。
  2. countDownLatch 一個線程A等待其餘線程執行完成以後才繼續執行。
  3. cyclicBarrier 一組線程等待至某個狀態以後同時執行。

countDownLatch和CyclicBarrier的區別

  1. countDownLatch是一個線程等一組線程執行完成以後才執行, cyclicBarrier是一組線程互相等待至某個狀態以後,同時執行。
  2. countDownLatch不能複用,cyclicBarrier能夠重用。

1六、鎖降級

鎖降級是指將寫鎖降級爲讀鎖,這個過程就是當前線程已經獲取到寫鎖的時候,再獲取到讀鎖,隨後釋放寫鎖的過程,這麼作的目的爲的就是保證數據的可見性。

1七、逃逸分析

  1. 逃逸分析就是分析對象的動態做用域,當一個對象在方法中被定義後,他可能被外部方法所引用,做爲參數傳遞到其餘方法中,成爲方法逃逸,賦值給類變量或者能夠被其餘線程訪問的實例變量成爲線程逃逸。
  2. 使用逃逸分析,編譯器能夠對代碼作優化。好比:同步省略(鎖消除),將堆分配轉化爲棧分配,標量替換。
  3. 使用逃逸分析的缺點,無法保證逃逸分析的性能必定高於其餘性能。極端的話通過逃逸分析後,全部的對象都逃逸了,那麼逃逸分析的過程就浪費了。

tencent.jpg

相關文章
相關標籤/搜索