日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

aqs原理初探以及公平鎖和非公平鎖實(shí)現(xiàn)

作者:huisheng_qaq 更新時間: 2022-07-12 編程語言

深入理解AQS

  • 一,AQS
    • 1,ReentrantLock
    • 2,CAS
    • 3,AbstractQueuedSynchronizer
      • 3.1,F(xiàn)airSync
      • 3.2,NofairSync
      • 3.3,AQS中幾個重要的相關(guān)參數(shù)
      • 3.4,Node

一,AQS

AbstractQueuedSynchronizer,定義了一套多線程訪問共享資源的同步器框架,依賴于狀態(tài)的同步器

1,ReentrantLock

一種基于AQS框架的應(yīng)用實(shí)現(xiàn),類似于synchronized是一種互斥鎖,可以保證線程安全。它支持手動加鎖與解鎖,支持加鎖的公平性。主要是Lock鎖的實(shí)現(xiàn)

public class ReentrantLock implements Lock, Serializable

接下來可以手動的猜想一下這個reentrantLock的實(shí)現(xiàn)

ReentrantLock lock = new ReentrantLock(true);
HashSet hashSet = new HashSet();
3個線程 T0,T1,T2
lock.lock(); //加鎖
	while(true){ //循環(huán),輪詢獲取鎖
		if(加鎖成功){ //synchronized,cas  cas:compare and swap
			break;//跳出循環(huán)
		}
		//Thread.yeild() //讓出cpu使用權(quán)
		//Thread.sleep(1000); //睡眠
        hashSet.add(thread); //將當(dāng)前線程加入到set中
		//阻塞
        LockSupprot.park();
	}
	T0獲取鎖
    xxxxx業(yè)務(wù)邏輯
    xxxxx業(yè)務(wù)邏輯   
lock.unlock();
Thread thread = hashSet.get();
//喚醒當(dāng)前線程,notify是喚醒隨機(jī)的線程
LockSupport.unPark(thread);

三大核心:自旋,加鎖,LockSupport,隊(duì)列(LinkQueue),為了解決這個公平鎖和非公平鎖,因此優(yōu)先考慮這個隊(duì)列。

2,CAS

compare and swap,比較與交換
在這里插入圖片描述

如在jmm模型中,兩個工作內(nèi)存都去修改主內(nèi)存的值。主內(nèi)存中存在一個a = 0,線程A和線程B的工作內(nèi)存同時獲取到這個值,如果線程A先修改這個值,則線程A會和主內(nèi)存比較,如果線程A的值a和主內(nèi)存的值一致,那么就會直接進(jìn)行修改,如改成a = 1,那么線程B也要改這個值,線程B中的a = 0,那么會和主內(nèi)存a比較,發(fā)現(xiàn)不一致,主內(nèi)存a=1,那么就會優(yōu)先將線程B中的值修改成a = 1,再對a進(jìn)行修改。就是說相等直接修改,不相等需要重新讀取,再進(jìn)行修改。即在一個原子操作里面進(jìn)行比較和替換

主要通過這個unsafe類實(shí)現(xiàn),里面的實(shí)現(xiàn)也是原子類操作,主要是通過以下三個類實(shí)現(xiàn)。

//對象類型
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
//整型值
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
//Long型值
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

3,AbstractQueuedSynchronizer

該類是ReentrantLock里面的一個抽象內(nèi)部類。ctrl + alt + shift + u看所有子類,Ctrl + T,看所有的繼承類,可以發(fā)現(xiàn)很多地方都繼承或者實(shí)現(xiàn)了這個抽象類

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-4dzYpLnW-1657115375965)(C:\Users\HULOUBO\AppData\Roaming\Typora\typora-user-images\1656947493769.png)]
Sync是ReentrantLock的一個抽象的靜態(tài)內(nèi)部類,根據(jù)圖也可以發(fā)現(xiàn)這個Sync繼承了這個AQS

abstract static class Sync extends AbstractQueuedSynchronizer

通過實(shí)現(xiàn)Sync這個接口得到了FairSync公平鎖類和NofairSync非公平鎖這個類

3.1,F(xiàn)airSync

實(shí)現(xiàn)了公平鎖,需要排隊(duì)獲取鎖,如存在線程t1,t2,t3依次獲取鎖,需要依次排隊(duì)執(zhí)行,突然來了一個線程t4,也是需要排在線程t3后面

ReentrantLock reentrantLock = new ReentrantLock(true);
public ReentrantLock(boolean fair) {
    //入?yún)ⅲ糜谂袛嗍枪芥i還是非公平鎖
    sync = fair ? new FairSync() : new NonfairSync();
}
//公平鎖的實(shí)現(xiàn)
static final class FairSync extends ReentrantLock.Sync{...}

公平鎖獲取鎖的方式如下

final void lock() {
    acquire(1);
}

public final void acquire(int arg) {
    //tryAcquire:嘗試去獲取鎖
    //addWaiter:線程入隊(duì),一個同步等待隊(duì)列,基于雙向列表實(shí)現(xiàn)
    //鏈表中的每一個結(jié)點(diǎn)為一個Node結(jié)點(diǎn),
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

//線程入隊(duì)操作
private Node addWaiter(Node mode) {
    //獲取當(dāng)前線程結(jié)點(diǎn)
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //結(jié)點(diǎn)入隊(duì)
    enq(node);
    return node;
}

//判斷是否獲取鎖
protected final boolean tryAcquire(int acquires) {
    //獲取當(dāng)前獲取鎖的線程
    final Thread current = Thread.currentThread();
    //獲取當(dāng)前同步器狀態(tài),被volatile修飾的整型值,默認(rèn)為0
    int c = getState();
    //如果同步狀態(tài)器的值為0,說明外面的線程可以進(jìn)行加鎖操作
    if (c == 0) {
        //hasQueuedPredecessors:判斷隊(duì)列中是否還有在排隊(duì)的節(jié)點(diǎn)
        //compareAndSetState:原子操作,比較與交換,進(jìn)行加鎖的操作,將state變量將0變?yōu)?
        //setExclusiveOwnerThread:設(shè)置獲取鎖的的線程擁有者
        if (!hasQueuedPredecessors() &&
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //如果狀態(tài)同步器不為0,可能由自己持有,也可能由別的線程持有鎖
    //重復(fù)加鎖,如定義一個全局鎖,出現(xiàn)了這個可重入鎖的問題
    else if (current == getExclusiveOwnerThread()) {
        //如果自己持有鎖的話,state+1即可,反正不等于0就可以
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //別的線程獲有鎖,直接返回
    return false;
}

總而言之就是說,公平鎖就是就是通過一個隊(duì)列實(shí)現(xiàn),需要進(jìn)行排隊(duì)的去獲取鎖資源。主要是通過這個state的資源狀態(tài)器來控制獲取鎖的擁有者,如果state為0,則表示隊(duì)列中的下一個線程可以去獲取鎖,并且通過cas的方式來保證鎖的安全并發(fā)問題。通過隊(duì)列的思想,來保證這個獲取鎖的公平性和有序性。

3.2,NofairSync

實(shí)現(xiàn)了非公平鎖,默認(rèn)為非公平鎖,會存在搶鎖的情況

ReentrantLock reentrantLock = new ReentrantLock(flase);
//如果不傳入?yún)?shù),默認(rèn)是非公平鎖
public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    //入?yún)ⅲ糜谂袛嗍枪芥i還是非公平鎖
    sync = fair ? new FairSync() : new NonfairSync();
}
//非公平鎖的實(shí)現(xiàn)
static final class NonfairSync extends ReentrantLock.Sync{...}

非公平鎖獲取鎖的方法和公平鎖類似,只是少了幾步使用隊(duì)列的幾個方法

3.3,AQS中幾個重要的相關(guān)參數(shù)

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-12BMqIQA-1657115375967)(C:\Users\HULOUBO\AppData\Roaming\Typora\typora-user-images\1657029597278.png)]

exclusiveOwnerThread:用于記錄當(dāng)前獨(dú)占模式下,獲取鎖的線程是誰

state:同步狀態(tài)器,默認(rèn)為0,表示當(dāng)前沒有線程獲取鎖,外面的線程可以來獲取鎖了

Node:雙向鏈表結(jié)構(gòu),是一個同步等待隊(duì)列,head:隊(duì)頭,tail:隊(duì)尾,prev前軀指針,next,后繼指針

waiteState:結(jié)點(diǎn)的什生命狀態(tài)

Lock鎖和synchronized鎖都是可重入鎖

3.4,Node

[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-Zfr5p0Ru-1657115375970)(C:\Users\HULOUBO\AppData\Roaming\Typora\typora-user-images\1657110491185.png)]
pre:前驅(qū)指針
next:后繼指針
waitStatues:每個結(jié)點(diǎn)都存在很多狀態(tài),這個主要是存儲結(jié)點(diǎn)的生命狀態(tài)

結(jié)點(diǎn)的幾個生命狀態(tài)如下

SIGNAL:-1     //可被喚醒
CANCELLED:1	  //代表異常,中斷引起的,需要被廢棄
CONDITION:-2  //條件等待
PROPAGATE:-3  //傳播
Init:0初始狀態(tài)

結(jié)點(diǎn)入隊(duì)順序如下,入隊(duì)時,將入隊(duì)結(jié)點(diǎn)得前驅(qū)指針指向鏈表的tail結(jié)點(diǎn),將tail節(jié)點(diǎn)的next節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn),并將當(dāng)前結(jié)點(diǎn)設(shè)置為tail尾指針結(jié)點(diǎn)。

private Node enq(final Node node) {
    //自旋
    for (;;) {
        Node t = tail;
        if (t == null) { // Must initialize
            //如果隊(duì)列為空,要防止出現(xiàn)多個線程的并發(fā)問題,結(jié)點(diǎn)直接放在隊(duì)頭
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            //保證這個入隊(duì)的安全性,防止入隊(duì)出現(xiàn)并發(fā)問題
            //結(jié)點(diǎn)入隊(duì)到隊(duì)尾
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

當(dāng)前結(jié)點(diǎn)前面有結(jié)點(diǎn)獲取鎖,當(dāng)前節(jié)點(diǎn)需要進(jìn)行阻塞park,線程要開始排隊(duì)等待。
結(jié)點(diǎn)在阻塞之前,還得嘗試獲取一次鎖。
a,如果結(jié)點(diǎn)可以獲取到鎖,即當(dāng)前節(jié)點(diǎn)為頭結(jié)點(diǎn)的下一個結(jié)點(diǎn),頭結(jié)點(diǎn)即將鎖被釋放,則把當(dāng)前結(jié)點(diǎn)作為頭結(jié)點(diǎn)。之前的頭結(jié)點(diǎn)就可以被GC回收了
b,如果結(jié)點(diǎn)不能獲取到鎖,那么當(dāng)前結(jié)點(diǎn)就要等待被喚醒
? (1),第一輪循環(huán)會去修改head狀態(tài),并且將waitState修改為sinal = -1可被喚醒狀態(tài)
? (2),第二輪,阻塞線程

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            //當(dāng)前節(jié)點(diǎn)的前驅(qū)指針指向的節(jié)點(diǎn)
            final Node p = node.predecessor();
            //如果當(dāng)前結(jié)點(diǎn)指向的該結(jié)點(diǎn)為頭部結(jié)點(diǎn),則不需要進(jìn)行阻塞
            if (p == head && tryAcquire(arg)) {
                //將當(dāng)前結(jié)點(diǎn)作為頭部結(jié)點(diǎn)
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

每個結(jié)點(diǎn)的生命狀態(tài)的變換如下。默認(rèn)的結(jié)點(diǎn)狀態(tài)為0,需要將節(jié)點(diǎn)狀態(tài)(waitState)轉(zhuǎn)化為-1可喚醒狀態(tài)。前一個節(jié)點(diǎn)中的waitStatus狀態(tài)記錄著后一個節(jié)點(diǎn)的生命狀態(tài)。

//pred:前驅(qū)節(jié)點(diǎn) node:當(dāng)前節(jié)點(diǎn)
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    //默認(rèn)為初始狀態(tài)0
    int ws = pred.waitStatus;
    //SIGNAL為-1,可被喚醒狀態(tài)
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //將頭結(jié)點(diǎn)初始狀態(tài)轉(zhuǎn)化為可喚醒狀態(tài)
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

在修改成可被喚醒的狀態(tài)之后,就進(jìn)行阻塞操作。

private final boolean parkAndCheckInterrupt() {
    //阻塞線程,并且判斷該線程是否是被中斷的
    LockSupport.park(this);
    return Thread.interrupted();
}

在頭結(jié)點(diǎn)釋放鎖的時候,也會發(fā)一個通知去告知下一個需要獲取鎖的線程來搶鎖,即喚醒隊(duì)列中的下一個被阻塞的線程

//真正的釋放鎖
public void unlock() {
    sync.release(1);
}
public final boolean release(int arg) {
    //嘗試釋放鎖
    if (tryRelease(arg)) {
        Node h = head;
        //因?yàn)榍耙粋€結(jié)點(diǎn)中存放后一個節(jié)點(diǎn)中的聲明狀態(tài),因此
        //如果頭結(jié)點(diǎn)的waitStatus不為0,就說明后一個結(jié)點(diǎn)是一個可被喚醒的狀態(tài)
        if (h != null && h.waitStatus != 0)
            //喚醒
            unparkSuccessor(h);
        return true;
    }
    return false;
}
//嘗試去釋放鎖
protected final boolean tryRelease(int releases) {
    //修改同步狀態(tài)器state
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //將同步狀態(tài)器state 變?yōu)?0,說明當(dāng)前同步狀態(tài)器可以進(jìn)行鎖的獲取了
    if (c == 0) {
        free = true;
        //置空當(dāng)前線程
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

最后在這個unparkSuccessor方法中,也有這個具體的unpark喚醒操作

if (s != null)
    LockSupport.unpark(s.thread)

通過上述代碼描述,也驗(yàn)證了一開始的猜想:自旋,加鎖,LockSupport,隊(duì)列(LinkQueue)

?

原文鏈接:https://blog.csdn.net/zhenghuishengq/article/details/125648495

欄目分類
最近更新