鎖的七大類
在Java的多線程中,有很多種鎖,每種鎖可能同時占有多個標准,比如ReentranLock即可是中斷鎖,又可以是可重入鎖。
那么根據分類標准,我們可以把這些鎖分為以下七大標准,分別是:
- 偏向鎖/輕量級鎖/重量級鎖
- 可重入鎖/非可重入鎖
- 共享鎖/獨占鎖
- 公平鎖/非公平鎖
- 悲觀鎖/樂觀鎖
- 自旋鎖/非自旋鎖
- 可中斷鎖/不可中斷鎖
偏向鎖/輕量級鎖/重量級鎖
這三種鎖特指 synchronized 鎖的狀態,通過在對象頭種的mark word來表明鎖的狀態。
偏向鎖
在被synchronized修飾的情況下,當前正在執行synchronized的線程,有且只有一個線程來獲取synchronized的鎖,這個時候系統就會給他升級為偏向鎖,因為前來獲取鎖的線程並沒有其他現在來競爭獲取,所以系統只需要給他打一個標記,告訴它下一次的鎖直接給他,這樣就可以直接獲得鎖,並且開銷也是最小的。
輕量級鎖
滿足輕量級鎖的條件
- 被synchronized進行修飾的鎖
- 被多個線程交替執行的,不是同時執行,不存在實際的競爭
- 只有短時間的鎖競爭
如果滿足上面三種情況,synchronized鎖的狀態就會從偏向鎖升級為輕量級鎖,線程會通過自旋的形式來獲取鎖,而不會陷入阻塞
重量級鎖
滿足重量級鎖的條件
- 被synchronized進行修飾的鎖
- 被多個線程交替執行的並且是同時執行
- 鎖的競爭時間較長
重量級鎖是互斥鎖,他利用的是操作系統的同步機制來實現的,所以開銷會比偏量鎖和輕量鎖的開銷都大,當滿足上面的條件后,輕量級鎖就會升級為重量級鎖,重量級鎖就會讓其他申請卻拿不到鎖的線程進入到阻塞狀態。
鎖升級路線
結合上面的描述,我們其實可以發現得到對於synchronized鎖的升級路線,並且性能也是由着鎖升級的推進,性能的開銷也會變的越來越大。
可重入鎖/非可重入鎖
可重入鎖:一個線程內可以同時獲取一把鎖多次
比如在線程A中有一把B鎖,如果A在獲取到B鎖的情況下,再次去獲取B鎖,如果可以獲取到B鎖,那么他就是一把可重入鎖。
例子:ReentrantLock
不可重入鎖:一個線程內只能同時擁有一把鎖一次
比如在線程A中有一把B鎖,如果A線程在獲取到B鎖的情況下,想再次去獲取到B鎖卻失敗了,需要釋放到之前的B鎖,然后再去獲取B鎖,那么這樣就是一個不可重入鎖。
共享鎖/獨占鎖
共享鎖:可以同時被多個線程獲取到鎖,例如我們常見的讀操作,它並不影響數據,所以可以被允許將鎖給多個線程同時訪問。
獨占鎖:有且只能被一個鎖獨占訪問,這種就常用到我們的寫操作上,由於需要保證數據的一致性和原子性。就需要在數據的寫操作上只能允許一個線程來訪問使用。
公平鎖/非公平鎖
公平鎖:遵循先來先得的規則,如果線程在搶不到鎖的情況下,會進入到排序的狀態中,最先排隊靠前的鎖就會最顯輪到獲取鎖。
非公平鎖:在公平鎖的規則基礎上,會在一定的情況下導致鎖插隊。
自旋鎖/非自旋鎖
自旋鎖的理念就是在線程拿不到鎖的時候,它並不會進入到阻塞狀態或者釋放掉CPU的資源,它是直接去循環的去訪問鎖的狀態,不斷的去嘗試獲取鎖,這個循環的過程也被叫做自旋。
非自旋鎖就跟自旋鎖相反,他沒獲取到鎖他就不會再去嘗試獲取,但是他會直接去排隊或者陷入阻塞狀態中。
可中斷鎖/不可中斷鎖
顧名思義,可中斷鎖就是在獲取到鎖后可以中斷他的任務然后去做其他的任務,最典型的就是使用ReentrantLock,它就是一種典型的可中斷鎖,例如使用lockInterruptibly方法再獲取鎖的過程中,突然不想獲取了,那么也可以在中斷之后去做其他的事情,不需要一直傻等到獲取鎖才離開
不可中斷鎖就沒有那么靈活,典型的就是synchronized,一旦申請了鎖,那么就沒有回頭路了,只能等到拿到鎖以后才能進行其他的邏輯處理。