??博客主頁(yè):?【小扳_-CSDN博客】
?感謝大家點(diǎn)贊??收藏?評(píng)論?
文章目錄
? ? ? ? 1.0 樂觀鎖與悲觀鎖概述
? ? ? ? 1.1 悲觀鎖(Pessimistic Locking)
? ? ? ? 1.2 樂觀鎖(Optimistic Locking)
? ? ? ? 1.3 區(qū)別與適用場(chǎng)景
? ? ? ? 2.0 輕量級(jí)鎖與重量級(jí)鎖概述
? ? ? ? 2.1 真正加鎖的底層邏輯順序
? ? ? ? 2.2?輕量級(jí)鎖
? ? ? ? 2.3?重量級(jí)鎖
? ? ? ? 2.4 區(qū)別于適用場(chǎng)景
? ? ? ? 2.5 輕量級(jí)鎖與樂觀鎖的區(qū)別、重量級(jí)鎖與悲觀鎖的區(qū)別
? ? ? ? 3.0 自旋鎖與掛起等待鎖概述
? ? ? ? 3.1 自旋鎖(Spin Lock)
? ? ? ? 3.2 掛起等待鎖(Suspend-Resume Lock)
? ? ? ? 3.3 自旋鎖是一種典型的輕量級(jí)鎖的實(shí)現(xiàn)方式
? ? ? ? 4.0 公平鎖與非公平鎖概述
? ? ? ? 4.1 公平鎖(Fair Lock)
? ? ? ? 4.2 非公平鎖(Unfair Lock)
? ? ? ? 5.0 可重入鎖與不可重入鎖概述
? ? ? ? 6.0 讀寫鎖與互斥鎖概述
? ? ? ? 6.1 讀寫鎖(Read-Write Lock)
? ? ? ? 6.2 互斥鎖(Mutex Lock)
? ? ? ? 7.0 CAS 概述
? ? ? ? 7.1 CAS 的實(shí)際應(yīng)用
? ? ? ? 7.1.1 CAS 的實(shí)際應(yīng)用 - 實(shí)現(xiàn)原子類
? ? ? ? 7.1.2?CAS 的實(shí)際應(yīng)用 - 自旋鎖
? ? ? ? 1.0 樂觀鎖與悲觀鎖概述
????????樂觀鎖和悲觀鎖是兩種并發(fā)控制的策略,用于處理多線程環(huán)境下的數(shù)據(jù)訪問和更新。它們的主要區(qū)別在于對(duì)并發(fā)情況的預(yù)期和處理方式。
? ? ? ? synchronized 是樂觀鎖也悲觀鎖。
? ? ? ? 1.1 悲觀鎖(Pessimistic Locking)
? ? ? ? 悲觀鎖的基本思想是在操作數(shù)據(jù)之前先獲取鎖,假定會(huì)并發(fā)訪問,因此在整個(gè)操作過程中都持有鎖,以防其他線程對(duì)數(shù)據(jù)進(jìn)行修改。悲觀鎖通常會(huì)導(dǎo)致其他線程在訪問數(shù)據(jù)時(shí)被阻塞,以確保數(shù)據(jù)的一致性。
? ? ? ? 簡(jiǎn)單來說,在多線程中對(duì)共享變量進(jìn)行操作時(shí),會(huì)認(rèn)為其他線程都會(huì)對(duì)這個(gè)共享變量進(jìn)行操作,因此,從悲觀鎖的角度來說,先加鎖成功后,才能對(duì)共享變量進(jìn)行操作;否則,只能阻塞等待鎖釋放。從而可以確保線程安全。
? ? ? ? 常見的悲觀鎖:synchronized 關(guān)鍵字、ReentrantLock 等鎖機(jī)制。
? ? ? ? 1.2 樂觀鎖(Optimistic Locking)
? ? ? ? 樂觀鎖的基本思想是假設(shè)在數(shù)據(jù)操作過程中不會(huì)發(fā)生并發(fā)沖突,因此不會(huì)立即加鎖,而是在更新數(shù)據(jù)時(shí)檢查是否有其他線程已經(jīng)對(duì)數(shù)據(jù)進(jìn)行修改了。如果沒有發(fā)現(xiàn)數(shù)據(jù)被修改,那么繼續(xù)操作;如果發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被修改了,會(huì)進(jìn)行回滾或者重試。
? ? ? ? 簡(jiǎn)單來說,在多線程中對(duì)共享變量進(jìn)行操作時(shí),一開始不會(huì)認(rèn)為有其他線程會(huì)對(duì)該共享變量進(jìn)行操作,認(rèn)為當(dāng)前線程可以安心的對(duì)該變量進(jìn)行操作。執(zhí)行到后面,如果發(fā)現(xiàn)已經(jīng)有其他線程對(duì)該共享變量進(jìn)行了操作了,那么當(dāng)前的線程執(zhí)行回滾或者重試。
? ? ? ? 1.3 區(qū)別與適用場(chǎng)景
? ? ? ? synchronized 一開始是使用樂觀鎖策略,當(dāng)發(fā)現(xiàn)鎖競(jìng)爭(zhēng)比較頻繁的時(shí)候,就會(huì)自動(dòng)切換悲觀鎖策略。
? ? ? ? 悲觀鎖:真正加上了鎖處理,適用于并發(fā)寫入較多、沖突概率較高的場(chǎng)景,適合長(zhǎng)事務(wù)處理。
? ? ? ? 樂觀鎖:沒有真正的加鎖處理,適用于并發(fā)讀取較多,沖突概率較低的場(chǎng)景,適合短事務(wù)處理。
? ? ? ? 2.0 輕量級(jí)鎖與重量級(jí)鎖概述
????????輕量級(jí)鎖和重量級(jí)鎖是 Java 中用于實(shí)現(xiàn)同步的兩種鎖機(jī)制,用于保護(hù)共享資源在多線程環(huán)境下的訪問。它們的設(shè)計(jì)目的是為了在不同情況下提供更高效的并發(fā)控制。
? ? ? ? synchronized 是輕量級(jí)鎖也是重量級(jí)鎖。
? ? ? ? 2.1 真正加鎖的底層邏輯順序
? ? ? ? CPU 提供了原子操作指令給操作系統(tǒng),操作系統(tǒng)提供了 mutex 互斥鎖給 JVM ,接著 JVM 將鎖封裝成了 synchronized 中的悲觀鎖、ReenTrantLock 等。
? ? ? ? 2.2?輕量級(jí)鎖
? ? ? ? 加鎖機(jī)制盡可能不適用 mutex ,而是盡量在用戶態(tài)代碼中完成,實(shí)在鎖競(jìng)爭(zhēng)太大、鎖沖突太大了,再轉(zhuǎn)換為重量級(jí),即使用 mutex 。
? ? ? ? 輕量級(jí)鎖是一種樂觀鎖機(jī)制,用于優(yōu)先低競(jìng)爭(zhēng)情況下的同步操作。當(dāng)一個(gè)線程嘗試獲取鎖是,如果鎖沒有被其他線程占用,會(huì)將對(duì)象的 Mark Word 指向當(dāng)前線程,將對(duì)象狀態(tài)標(biāo)記為“偏向鎖”。
? ? ? ? 2.3?重量級(jí)鎖
? ? ? ? 加鎖機(jī)制重度依賴了 OS 提供了 mutex ,大量的內(nèi)核態(tài)用戶態(tài)切換,很容易引發(fā)線程的調(diào)度。兩個(gè)操作成本比較高,一旦涉及到用戶態(tài)和內(nèi)核態(tài)的切換,就意味著“滄海桑田”。
? ? ? ? 重量級(jí)鎖是一種悲觀鎖機(jī)制,用于處理高競(jìng)爭(zhēng)情況下的同步操作。當(dāng)多個(gè)線程競(jìng)爭(zhēng)同一把鎖時(shí),會(huì)將鎖升級(jí)為重量級(jí)鎖,線程會(huì)被阻塞,進(jìn)入阻塞狀態(tài)。
? ? ? ? 2.4 區(qū)別于適用場(chǎng)景
? ? ? ? synchronized 開始是一個(gè)輕量級(jí)鎖。如果鎖沖突比較嚴(yán)重,就會(huì)變成重量級(jí)鎖。
????????輕量級(jí)鎖:適用于低競(jìng)爭(zhēng)情況下的同步操作,提高了并發(fā)性能,但在高競(jìng)爭(zhēng)情況下會(huì)升級(jí)為重量級(jí)鎖。
????????重量級(jí)鎖:適用于高競(jìng)爭(zhēng)情況下的同步操作,保證了數(shù)據(jù)的一致性,但在低競(jìng)爭(zhēng)情況下會(huì)帶來額外的開銷。
在實(shí)際應(yīng)用中,Java虛擬機(jī)會(huì)根據(jù)當(dāng)前線程的競(jìng)爭(zhēng)情況動(dòng)態(tài)地選擇輕量級(jí)鎖或重量級(jí)鎖來進(jìn)行同步操作,以提高系統(tǒng)的并發(fā)性能和數(shù)據(jù)一致性。
? ? ? ? 2.5 輕量級(jí)鎖與樂觀鎖的區(qū)別、重量級(jí)鎖與悲觀鎖的區(qū)別
輕量級(jí)鎖:
????????輕量級(jí)鎖是一種樂觀鎖,它嘗試使用 CAS(Compare and Swap)等原子操作來嘗試獲取鎖,避免了線程阻塞和內(nèi)核態(tài)操作,因此被稱為輕量級(jí)。如果 CAS 操作成功,線程就成功獲取了鎖,如果失敗,則會(huì)升級(jí)為重量級(jí)鎖或其他適合的鎖機(jī)制。輕量級(jí)鎖并不是沒有真正的加鎖,而是通過樂觀的方式嘗試獲取鎖,避免了一些開銷較大的操作。
重量級(jí)鎖:
????????重量級(jí)鎖是一種悲觀鎖,它通常會(huì)涉及到線程的阻塞、喚醒和操作系統(tǒng)的調(diào)度等操作,因此被稱為重量級(jí)。當(dāng)多個(gè)線程競(jìng)爭(zhēng)鎖時(shí),重量級(jí)鎖會(huì)導(dǎo)致線程進(jìn)入阻塞狀態(tài),等待其他線程釋放鎖后才能繼續(xù)執(zhí)行。重量級(jí)鎖會(huì)涉及到真正的加鎖操作,包括線程的阻塞和喚醒等。
? ? ? ? 3.0 自旋鎖與掛起等待鎖概述
????????自旋鎖和掛起等待鎖是兩種不同的鎖機(jī)制,它們?cè)谔幚砭€程同步和互斥時(shí)有不同的實(shí)現(xiàn)方式和特點(diǎn)。
? ? ? ? synchronized 是自旋鎖也是掛起鎖。
? ? ? ? 3.1 自旋鎖(Spin Lock)
? ? ? ? 自旋鎖是一種基于忙等待的鎖機(jī)制,當(dāng)一個(gè)線程嘗試獲取鎖時(shí),如果發(fā)現(xiàn)鎖已經(jīng)被其他線程占用,它會(huì)一直循環(huán)檢查鎖的狀態(tài)(自旋)直到鎖可用。自旋鎖適用于鎖被占用時(shí)間較短的情況,因?yàn)樗捎脺p少線程切換的開銷。但是如果鎖被長(zhǎng)時(shí)間占用,自旋鎖會(huì)導(dǎo)致線程長(zhǎng)時(shí)間占用 CPU 資源而無法進(jìn)展,造成性能問題。
? ? ? ? 簡(jiǎn)單來說,自旋鎖一直會(huì)占用 CPU 資源,所謂的“空轉(zhuǎn)”、“忙等待”,只要鎖被釋放了,那么自旋鎖就會(huì)立馬獲取鎖,效率高。
? ? ? ? 按照之前的方式,線程再搶鎖失敗后,進(jìn)入阻塞狀態(tài),放棄 CPU ,需要過很久再次被調(diào)度,但實(shí)際上,大部分情況下,雖然當(dāng)前線程搶鎖失敗,但過不了多久,鎖就會(huì)被釋放。這樣就沒有必要放棄 CPU 資源。這時(shí)候就可以適用自旋鎖來處理這樣的問題。
? ? ? ? 3.2 掛起等待鎖(Suspend-Resume Lock)
? ? ? ? 掛起等待鎖是一種基于線程阻塞和喚醒的鎖機(jī)制,當(dāng)一個(gè)線程嘗試獲取鎖時(shí),如果發(fā)現(xiàn)鎖已經(jīng)被占用,它會(huì)被掛起阻塞等待其他線程釋放鎖。當(dāng)鎖可用時(shí),其他線程會(huì)喚醒被掛起的線程繼續(xù)執(zhí)行。
? ? ? ? 掛起等待鎖適用于鎖被占用時(shí)間長(zhǎng)的情況,因?yàn)樗梢员苊饩€程忙等待占用 CPU 資源,但是會(huì)引入線程切換的開銷。
? ? ? ? 3.3 自旋鎖是一種典型的輕量級(jí)鎖的實(shí)現(xiàn)方式
? ? ? ? synchronized 中的輕量級(jí)鎖策略大概率就是通過自旋鎖的方式實(shí)現(xiàn)的。
優(yōu)點(diǎn):沒有放棄 CPU ,不涉及線程阻塞和調(diào)度,一旦鎖被釋放,就能第一時(shí)間獲取到鎖。
缺點(diǎn):如果鎖被其他線程持有的時(shí)間比較長(zhǎng),那么就會(huì)持續(xù)的消耗 CPU 資源。(而掛起等待的時(shí)候是不會(huì)消耗 CPU 資源的)。
? ? ? ? 相對(duì)應(yīng)的,掛起等待鎖是一種典型的重量級(jí)鎖的實(shí)現(xiàn)方式。
? ? ? ? 4.0 公平鎖與非公平鎖概述
????????公平鎖和非公平鎖是兩種不同的鎖策略,它們主要影響了鎖的獲取順序和公平性。
? ? ? ? synchronized 是非公平鎖。ReenTrantLock 默認(rèn)是非公平鎖,可以轉(zhuǎn)換成公平鎖。
ReentrantLock fairLock = new ReentrantLock(true); // 創(chuàng)建公平鎖
????????通過將參數(shù)設(shè)置為 true,可以創(chuàng)建一個(gè)公平鎖;而默認(rèn)情況下參數(shù)為 false,創(chuàng)建的是非公平鎖。
? ? ? ? 4.1 公平鎖(Fair Lock)
? ? ? ? 公平鎖是一種保證鎖的獲取按照請(qǐng)求的順序進(jìn)行的鎖。當(dāng)一個(gè)線程請(qǐng)求一個(gè)公平鎖時(shí),如果鎖當(dāng)前被其他線程占用,該線程會(huì)進(jìn)入等待隊(duì)列,按照先來先服務(wù)的原則等待獲取鎖。當(dāng)鎖釋放時(shí),等待時(shí)間最長(zhǎng)的線程會(huì)被喚醒并獲取鎖。公平鎖能夠保證線程按照請(qǐng)求的順序獲取鎖,避免了線程饑餓的問題。
? ? ? ? 4.2 非公平鎖(Unfair Lock)
? ? ? ? 非公平鎖是一種允許鎖獲取競(jìng)爭(zhēng)策略,它允許新請(qǐng)求的線程直接嘗試獲取鎖,而不考慮等待隊(duì)列中的線程順序。如果鎖當(dāng)前被其他線程占用,新請(qǐng)求的線程會(huì)直接嘗試獲取鎖,而不會(huì)進(jìn)入等待隊(duì)列。這種策略可能會(huì)導(dǎo)致某些線程長(zhǎng)時(shí)間無法獲取鎖,造成線程饑餓的問題。
? ? ? ? 5.0 可重入鎖與不可重入鎖概述
? ? ? ? 可重入鎖的字面意思是“可以重新進(jìn)入的鎖”,即允許同一個(gè)線程多次獲取同一把鎖。比如一個(gè)遞歸函數(shù)里面加鎖操作,遞歸過程中這個(gè)鎖會(huì)阻塞自己?jiǎn)??如果不?huì),那么這個(gè)鎖就是可重入鎖(因?yàn)檫@個(gè)原因可重入鎖也叫做遞歸鎖)。
? ? ? ? Java 里只要以 ReenTrant 開頭命名的鎖都是可重入鎖,而且 JDK 提供的所有現(xiàn)成的 Lock 實(shí)現(xiàn)類,包括 synchronized 關(guān)鍵字鎖都是可重入鎖。
? ? ? ? 而 Linux 系統(tǒng)提供的 mutex 是不可重入鎖。
? ? ? ? 6.0 讀寫鎖與互斥鎖概述
? ? ? ? 讀寫鎖簡(jiǎn)單來說,讀操作與讀操作并發(fā)執(zhí)行中不會(huì)加鎖,讀操作與寫操作并發(fā)操作中會(huì)加鎖,寫操作與寫操作并發(fā)操作中會(huì)加鎖。
? ? ? ? 互斥鎖簡(jiǎn)單來說,無論進(jìn)行哪一種操作,并發(fā)執(zhí)行的操作都需要加上鎖。
? ? ? ? synchronized 不是讀寫鎖,是互斥鎖。
????????ReenTrantLock 不是讀寫鎖,是互斥鎖。
ReenTrantLock 使用代碼演示:
import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockExample { private final ReentrantLock lock = new ReentrantLock(); private int sharedData = 0; public void incrementData() { lock.lock(); try { sharedData++; } finally { lock.unlock(); } } public static void main(String[] args) { ReentrantLockExample example = new ReentrantLockExample(); // 創(chuàng)建多個(gè)線程并發(fā)執(zhí)行 incrementData 方法 Thread thread1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.incrementData(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.incrementData(); } }); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final shared data value: " + example.sharedData); } }
? ? ? ? 6.1 讀寫鎖(Read-Write Lock)
? ? ? ? 讀寫鎖允許多個(gè)線程同時(shí)讀取共享資源,但在有寫操作時(shí)需要互斥訪問。
讀操作:
? ? ? ? 多個(gè)線程可以同時(shí)獲取讀鎖,并發(fā)執(zhí)行讀操作,不會(huì)互斥。
寫操作:
? ? ? ? 寫鎖是互斥的,即寫操作與任何其他操作或?qū)懖僮鞫际腔コ獾?。?dāng)有線程持有寫鎖時(shí),其他線程無法獲取讀鎖或者寫鎖,直到寫操作釋放寫鎖。
????????ReentrantReadWriteLock.ReadLock 類表示一個(gè)讀鎖,這個(gè)對(duì)象提供了 lock/unlock 方法進(jìn)行加鎖解鎖。
????????ReentrantReadWriteLock.WriteLock 類表示一個(gè)寫鎖,這個(gè)對(duì)象提供了 lock/unlock 方法進(jìn)行加鎖解鎖。
代碼如下:
import java.util.concurrent.locks.ReentrantReadWriteLock; public class MyReentrantLock { private int data = 100; private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock(); public int readData(){ readLock.lock(); try { return data; }finally { readLock.unlock(); } } public void writeData(int data){ writeLock.lock(); try { this.data = data; }finally { writeLock.unlock(); } } }
public class demo1 { public static void main(String[] args) { MyReentrantLock myReentrantLock = new MyReentrantLock(); //讀取數(shù)據(jù) for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "-->讀取數(shù)據(jù)為: " + myReentrantLock.readData()); }).start(); } //寫數(shù)據(jù) for (int i = 0; i < 100; i++) { new Thread(()->{ myReentrantLock.writeData(1); }).start(); } for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName() + "-->讀取數(shù)據(jù)為: " + myReentrantLock.readData()); }).start(); } } }
? ? ? ? 6.2 互斥鎖(Mutex Lock)
? ? ? ? 互斥鎖是一種常見的鎖機(jī)制,用于保護(hù)共享資源的互斥訪問。無論是讀操作還是寫操作,都要獲取互斥鎖才能訪問共享資源。
? ? ? ? 7.0 CAS 概述
????????CAS(Compare and Swap)是一種并發(fā)控制機(jī)制,通常用于實(shí)現(xiàn)無鎖算法。它主要用于解決多線程并發(fā)訪問共享數(shù)據(jù)時(shí)的原子性操作問題。
????????CAS 的基本原理是利用 CPU 提供的原子性指令來實(shí)現(xiàn)無鎖的原子操作。當(dāng)多個(gè)線程同時(shí)嘗試執(zhí)行 CAS 操作時(shí),只有一個(gè)線程會(huì)成功,其他線程會(huì)失敗并重試。
CAS 操作包括三個(gè)步驟:
? ? ? ? 1)比較(Compare):首先, CAS 會(huì)比較當(dāng)前內(nèi)存中的值和預(yù)期值是否相等。
? ? ? ? 2)交換(Swap):如果相等,CAS 就會(huì)將新值寫入到主內(nèi)存中;否則,不做任何操作。
? ? ? ? 3)返回(Return):CAS 操作會(huì)返回操作是否成功的結(jié)果,通常時(shí)一個(gè)布爾值。
? ? ? ? 這三個(gè)操作都是原子性的,因此不存在線程安全問題。
圖解:
? ? ? ? 7.1 CAS 的實(shí)際應(yīng)用
? ? ? ? 原子操作、非阻塞算法、自旋鎖、ABA 問題的解決、樂觀鎖的實(shí)現(xiàn)等。
? ? ? ? 7.1.1 CAS 的實(shí)際應(yīng)用 - 實(shí)現(xiàn)原子類
????????CAS 可以用于實(shí)現(xiàn)原子操作,比如 AtomicInteger、AtomicLong 等原子類都是基于 CAS 實(shí)現(xiàn)的。在多線程環(huán)境下,通過 CAS 可以確保對(duì)共享變量的操作是原子的,避免了使用鎖帶來的性能開銷。
? ? ? ? 比如說 AtomicInteger 類,是基于 CAS 的思想實(shí)現(xiàn)的,假如有一個(gè) AtomicInteger 的實(shí)例對(duì)象,在多線程中,對(duì)該實(shí)例對(duì)象進(jìn)行 +1 操作,一般來說,如果不加鎖的話,會(huì)出現(xiàn)線程安全問題,但是對(duì)于當(dāng)前對(duì)象來說,即使不用加鎖,也不用出現(xiàn)線程安全問題。
代碼如下:
import java.util.concurrent.atomic.AtomicInteger; public class MyAtomicInteger { private AtomicInteger count = new AtomicInteger(0); public void add(){ System.out.println(count.incrementAndGet()); } }
public class Text { public static void main(String[] args) { MyAtomicInteger myAtomicInteger = new MyAtomicInteger(); for (int i = 0; i < 1000; i++) { new Thread(()->{ for (int j = 0; j < 5000; j++) { myAtomicInteger.add(); } }).start(); } } }
運(yùn)行結(jié)果如下:
? ? ? ? 運(yùn)行結(jié)果是正確的,沒有出現(xiàn)線程安全問題。
? ? ? ? 這是為什么即使沒有加上鎖也不會(huì)出現(xiàn)線程安全問題呢?
? ? ? ? 答案就在用了 AtomicInteger 修飾的變量,且 +1 操作用到了 count.incrementAndGet() 實(shí)例方法。
詳細(xì)對(duì)?count.incrementAndGet() 方法進(jìn)行分析:
? ? ? ? 該方法中還包含了 getAndAddInt() 方法,第一個(gè)參數(shù)是代表著當(dāng)前對(duì)象,第二個(gè)參數(shù)可以認(rèn)為是存放值的地址,第三個(gè)參數(shù)默認(rèn)為 1 。
進(jìn)入 getAndAddInt() 方法進(jìn)行分析:
? ? ? ? ?參數(shù) o 代表著當(dāng)前對(duì)象,參數(shù) offset 代表著值的地址,參數(shù) delta 為 1 。該方法中內(nèi)部定義了一個(gè)變量 v ,通過 getIntVolatile() 這個(gè)方法,用當(dāng)前的對(duì)象還有值的地址獲取到最新的數(shù)據(jù)賦值給 v 。再接著通過 weakCompareAndSetInt() 方法,來比較當(dāng)前的 v 跟之前獲取的 v 的值是否相同,如果相同,代表著沒有線程訪問這個(gè)數(shù)據(jù),只有當(dāng)前線程正在訪問,那么就可以對(duì)這個(gè)數(shù)據(jù)進(jìn)行修改,再返回到主內(nèi)存中;如果不相同,代表有其他線程訪問這個(gè)數(shù)據(jù),此時(shí)不能直接將當(dāng)前線程更新的值放到主內(nèi)存中,會(huì)出現(xiàn)線程安全問題,因此重復(fù)循環(huán),再來新一輪,先獲取主內(nèi)存中最新的數(shù)據(jù),在來比較當(dāng)前數(shù)據(jù)與之前獲取到的 v 是否相同...一直循環(huán)往復(fù)。直到當(dāng)前數(shù)據(jù)與之前的獲取到的 v 相同,那么就可以將值放入到內(nèi)存中。
進(jìn)入 weakCompareAndSetInt() 方法進(jìn)行分析:
? ? ? ? 如果成功就返回 true,否則返回 false 。?
? ? ? ? 最后,可以清楚的了解到以上這個(gè)思想跟 CAS 的機(jī)制是一致的。
? ? ? ? 7.1.2?CAS 的實(shí)際應(yīng)用 - 自旋鎖
? ? ? ? 自旋鎖是基于 CAS 機(jī)制實(shí)現(xiàn)更靈活的鎖,獲取到更多的控制權(quán)。
偽代碼:
public class MySpinLock { private Thread ower = null; public void lock(){ //通過 CAS 看當(dāng)前鎖是否被某個(gè)線程持有 //如果這個(gè)鎖已經(jīng)被別的線程持有,那么就自旋等待。 //如果這個(gè)鎖沒有被別的線程持有,那么就把 ower 設(shè)為當(dāng)前嘗試加鎖的線程。 while (!CAS(this.ower,null,Thread.currentThread())){ } } public void unlock(){ this.ower = null; } }
? ? ? ? 結(jié)合自旋鎖的特點(diǎn)和 CAS 機(jī)制來分析,線程只要沒有獲取的鎖,就會(huì)一直占用 CPU 資源等待,直到鎖釋放為止,如何來判斷鎖是否被占用呢?
? ? ? ? 就可以通過 CAS 機(jī)制來判斷,大概流程是:判斷當(dāng)前的線程 ower 是否為 null ,如果是,則將 ower 修改為當(dāng)前線程所持有,這樣來看,其他線程也會(huì)通過 CAS 機(jī)制來判斷當(dāng)前的 ower 是否否為 null ,返回結(jié)果為 false ,則只能空轉(zhuǎn)了,等待當(dāng)前線程釋放鎖,此時(shí)釋放鎖會(huì)把 ower 賦值為 null 。交給其他線程來獲取這把“鎖”。?文章來源:http://www.zghlxwxcb.cn/news/detail-853034.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-853034.html
到了這里,關(guān)于JavaEE 初階篇-深入了解 CAS 機(jī)制與12種鎖的特征(如樂觀鎖和悲觀鎖、輕量級(jí)鎖與重量級(jí)鎖、自旋鎖與掛起等待鎖、可重入鎖與不可重入鎖等等)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!