tft每日頭條

 > 職場

 > java中關于鎖的面試題

java中關于鎖的面試題

職場 更新时间:2024-12-20 13:57:05

  溪雲閣:專注編程教學,架構,JAVA,Python,微服務,機器學習等領域,歡迎關注,一起學習。

  前言 雖然很多時候用到鎖的機會不大,但是鎖的問題在面試中經常會遇到,特别是互聯網公司,面對很多高并發的時候,摳細節就成了日常,但是平時在鎖的文章上都寫得非常深奧,難以消化,筆者整理了目前JAVA裡面的鎖,用最通俗易懂的話讓大家快速記憶。

  樂觀鎖 java中關于鎖的面試題(用最通俗易懂的語言)(1)

  樂觀鎖是我們經常無意間用到的東西,是一種樂觀思想,這種樂觀思想就是認為:當前環境讀數據的多,寫數據的少,并發讀多,并發寫少。因此,在讀數據的時候,并不會給當前線程加鎖,在寫數據的時候,會進行判斷當前的值與期望值時候相同,如果相同則進行更新,更新期間進行加鎖,保證原子性

  這個理論應該很多人會比較熟悉,CAS理論,比較并替換,在數據庫設計中經常采用version版本号來進行樂觀鎖的實現。

  悲觀鎖 java中關于鎖的面試題(用最通俗易懂的語言)(2)

  相比于樂觀鎖,悲觀鎖是一種非常悲觀的思想,遇到事總是想到最壞的情況,認為寫多讀少,因此無論是讀取數據還是寫入數據,都會當作要修改其他裡面的數據,通通上鎖,指導這個線程釋放鎖後其他線程獲取。

  在java裡面悲觀鎖有兩種實現:synchronizedReentrantLock

  自旋鎖 java中關于鎖的面試題(用最通俗易懂的語言)(3)

  原理:為了讓線程進行等待,讓線程不斷執行一個空操作的循環,類似你去找一個朋友,朋友在家裡幹活讓你等一下,你就在門口徘徊,不去幹别的事,徘徊了N次之後發現還沒來人,直接先去幹别的事,等他打電話叫你。

  優點: 主要是為了避免線程的挂起跟喚醒的開銷,因為這部分的開銷都需要在系統的内核态中完成,然後反饋到虛拟機,這樣子的操作對虛拟機并發性能帶來了巨大的壓力。

  缺點: 既然是執行空操作,必然會占用處理器的時間,當占用的時間過長的時候,處理器的資源會被白白消耗掉,而且這部分消耗是一直在做沒有任何意義的工作,性能上是非常浪費的。面對這種情況,等待的時間必須有一定的限度,如果自旋超過了限定的次數仍然沒有成功獲得鎖,就應當使用傳統的方式去挂起線程。

  默認值:JVM默認值10次,配置參數為:-XX:PreBlockSpin

  遞歸鎖(可重入鎖) java中關于鎖的面試題(用最通俗易懂的語言)(4)

  原理:任何線程獲取了鎖之後可以再次獲取該鎖而不會被阻塞,識别獲取鎖的線程是否為當前占據鎖的線程,如果是則再次成功獲取。獲取鎖後進行自增,

  優點: 可以避免死鎖。

  實現:synchronizedReentrantLock

  讀寫鎖 java中關于鎖的面試題(用最通俗易懂的語言)(5)

  讀寫鎖是通過ReentrantReadWriteLock這個類來實現,在JAVA裡面,為了提高性能而提供了這麼個東西,讀的地方用讀鎖,寫的地方用寫鎖,讀鎖并不互斥,讀寫互斥,這部分直接由JVM進行控制。

  在編碼上,需要手動進行區分,下面的代碼可以看到實現方式

  // 創建一個讀寫鎖 private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); // 獲取讀鎖 rwLock.readLock().lock(); // 釋放讀鎖 rwLock.readLock().unlock(); // 創建一個寫鎖 rwLock.writeLock().lock(); // 寫鎖 釋放 rwLock.writeLock().unlock();

  公平鎖 java中關于鎖的面試題(用最通俗易懂的語言)(6)

  公平鎖是一種設計思想,多線程在進行數據請求的過程中,先去隊列中申請鎖,按照FIFO先進先出的原則拿到線程,然後占有鎖。

  非公平鎖 java中關于鎖的面試題(用最通俗易懂的語言)(7)

  既然有公平鎖,那就有非公平鎖,也是一種設計思想。線程嘗試獲取鎖,如果獲取不到,這時候采用公平鎖的方式進行,與此同時,多個線程獲取鎖的順序有一定的随機性,并非按照先到先得的方式進行。

  優點:性能上高于公平鎖

  缺點:存在線程饑餓問題,存在某一個線程一直獲取不到鎖導緻一直等待,“餓死了”

  在java裡面,synchronized默認就是非公平鎖,ReentrantLock可以通過構造函數來設置該鎖是公平的還是非公平的,默認是非公平的。

  private final ReentrantLock.Sync sync; public ReentrantLock() { this.sync = new ReentrantLock.NonfairSync(); } public ReentrantLock(boolean fair) { this.sync = (ReentrantLock.Sync)(fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync()); }

  共享鎖 多個線程可以獲取讀鎖,以共享的形式持有,本質上與樂觀鎖,讀寫鎖一樣,JAVA的共享鎖也是ReentrantReadWriteLock

  獨占鎖 隻有一個線程可以獲取鎖,與悲觀鎖,互斥鎖一樣,JAVA的獨占鎖有:synchronizedReentrantLock

  重量級鎖 java中關于鎖的面試題(用最通俗易懂的語言)(8)

  重量級鎖其實是一種稱呼,synchronized就是一種重量級鎖,它是通過内部一個叫做監視器鎖來實現,而監視器鎖本質上是依賴于系統的Mutex Lock(互斥鎖)來實現,當加鎖的時候需要用用戶态切換為核心态,這樣子的成本非常高,因此這種依賴于操作系統Mutex Lock的鎖稱為重量級鎖。為了優化synchronized的性能,引入了輕量級鎖,偏向鎖。

  輕量級鎖 java中關于鎖的面試題(用最通俗易懂的語言)(9)

  在JDK1.6的時候,為了優化重量級鎖,引入了一種優化機制:輕量級鎖。由于鎖的獲取默認采用重量級,互斥的開銷很大,因此在沒有競争的時候采用CAS去操作以便消除同步使用的互斥鎖。

  優點:在沒有資源競争的情況下,通過CAS操作避免了互斥鎖的開銷

  缺點:如果存在競争,此時會額外增加CAS的開銷,此時導緻輕量級鎖比傳統重量級鎖更慢。

  偏向鎖 java中關于鎖的面試題(用最通俗易懂的語言)(10)

  除了輕量級鎖,JDK1.6還加入了另外一種鎖優化機制,偏向鎖。偏向鎖裡面最重要的一個理解就是:偏心。這個鎖會非常偏心對待第一個獲得它的線程,如果在接下來的執行過程中,該鎖一直沒有被其他的線程獲取,則持有偏向鎖的線程将永遠不需要再進行同步。

  優點:針對第一個線程,連CAS都不用做了,性能上強于輕量級鎖

  缺點:如果程序中的鎖總是被不同線程訪問,那這個偏向鎖就是多餘的,永遠都有第一個。

  分段鎖 分段鎖算是面試中經常會被問到,希望各位記住。

  在java裡面最好的實現就是ConcurrentHashMap,它裡面劃分了非常多的HashMap,默認是16個,如果需要添加一個key-value,并不是将整個HashMap鎖住,而是先進行hashcode計算從而得出這個key-value應該放在哪個HashMap裡面,然後開始對該HashMap進行加鎖,并完成put操作。在多線程中,想象一下同時進行的時候,是不是做到了真正意義上的同步進行。在這裡為了方便裡面,我用HashMap來代替Segment,其實兩者是一樣的東西,隻不過Segment是繼承了ReentrantLock來進行加鎖,非常優秀的設計。

  互斥鎖 互斥鎖用最簡單的一句話來理解:某個資源隻能被一個線程訪問,讀讀,讀寫,寫讀,寫寫都是一樣的。

  同步鎖 與互斥鎖一樣,在同一個時間隻允許一個線程訪問一個資源,實現用synchronized

  死鎖 死鎖并不是一種思想或者技術,而是一種狀态,當線程A持有資源a,線程B持有資源b,線程A等着B釋放b,線程B等着線程A釋放a,進入了死循環,造成死鎖。

  總結 JAVA裡面主要有ReentrantLock ,synchronized,Lock三種,類别也是不一樣

  synchronized:屬于獨占鎖、悲觀鎖、可重入鎖、非公平鎖

  ReentrantLock:繼承了Lock類,可重入鎖、悲觀鎖、獨占鎖、互斥鎖、同步鎖。

  Lock:Java中的接口,可重入鎖、悲觀鎖、獨占鎖、互斥鎖、同步鎖

  --END--

  作者:@溪雲閣

  原創作品,抄襲必究

  如需要源碼,請轉發,關注後私信我

  部分圖片或代碼來源網絡,如侵權請聯系删除,謝謝!

  ,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关職場资讯推荐

热门職場资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved