多線程8 讀寫鎖ReentrantReadWriteLock加解鎖

讀鎖不可以升級,寫鎖可以降級?

讀鎖是可並行的,寫鎖是串行的,那麼如果多個讀鎖並行執行,遇到升級語句,就會出現死鎖,比如t1要升級,那麼就要等t2釋放鎖,而t2正好也在當t1釋放鎖。

加鎖:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
// 獲取當前線程
    Thread current = Thread.currentThread();
// 線程計數
    int c = getState();
// 獲取獨佔鎖,就是寫鎖
    int w = exclusiveCount(c);
// 線程計數!=0,表示重入;這裏有兩種:讀鎖,寫鎖
    if (c != 0) {
//  如果是讀鎖,或者當前線程並非加鎖線程,返回false,就會進入acquireQueued(addWaiter(Node.EXCLUSIVE), arg))獲取鎖
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
// 判斷寫鎖的可重入次數是否超過MAX_COUNT,超過拋異常 
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
// 到這裏,確定是寫鎖且當前線程已持有鎖,重入鎖+1
        setState(c + acquires);
        return true;
    }
// 走到這裏,表示c=0,即沒有寫鎖,也沒有讀鎖
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}
final boolean writerShouldBlock() {
// 這個方法就是上一篇鎖講過的,查詢是否有線程等待,有就排隊
    return hasQueuedPredecessors();
}
if (writerShouldBlock() ||
// 設置線程計數
        !compareAndSetState(c, c + acquires))
        return false;
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
// 然後進入這個方法,上一篇講過
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

這裏就是,隊列裏排隊獲取鎖,和reentrantLock一樣。

讀鎖:

public void lock() {
    sync.acquireShared(1);
}
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
// 判斷是否獨佔鎖,是否當前線程;這裏如果出現獨佔鎖,那麼該線程就返回-1,去排隊
// 爲什麼呢?因爲在讀鎖加鎖的時候,也可能出現寫鎖進來,如果寫鎖進來了,寫鎖是排他鎖,獨佔一把鎖,那麼讀鎖也要去排隊
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
// 非獨佔鎖(寫鎖),獲取計數
    int r = sharedCount(c);
// 沒有線程等待,
    if (!readerShouldBlock() &&
// 且讀鎖計數小於最大值,
        r < MAX_COUNT &&
// 且加鎖成功
        compareAndSetState(c, c + SHARED_UNIT)) {
// 讀鎖計數爲0,沒有讀鎖,第一個線程進來
        if (r == 0) {
// 就給第一個線程對象賦值,
            firstReader = current;
            firstReaderHoldCount = 1;
// 如果說第一個線程對象等於當前線程對象,就是重入鎖
        } else if (firstReader == current) {
// 那麼第一個線程內部計數+1
            firstReaderHoldCount++;
// 如果以上條件都否定,即不是第一個線程,也不是重入鎖
        } else {
// 第一次進來,重入鎖計數對象 = null
            HoldCounter rh = cachedHoldCounter;
// 當前緩存中還沒有,或者是第二次進來,rh不會空,那麼判斷rh的線程id是否和當前線程id相同,不同則表示其他線程進入
            if (rh == null || rh.tid != getThreadId(current))
// 拿到緩存的重入鎖對象:如果是同一個線程進入,就返回那個線程的緩存計數對象,如果是其他線程,就會初始化一個返回
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
// 緩存對象計數+1
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

當前線程可重入鎖的數量是內部是線程變量實現;

它自己本身保存了一份ThreadLocalMap對象,用於保存重入鎖的線程

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
// 有當前線程
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
// 拿不到就初始化一個
    return setInitialValue();
}

假設第三個線程:

protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    int c = getState();
// 判斷是否獨佔鎖,是否當前線程;這裏如果出現獨佔鎖,那麼該線程就返回-1,去排隊
// 爲什麼呢?因爲在讀鎖加鎖的時候,也可能出現寫鎖進來,如果寫鎖進來了,寫鎖是排他鎖,獨佔一把鎖,那麼讀鎖也要去排隊
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
// 非獨佔鎖(寫鎖),獲取計數
    int r = sharedCount(c);
// 沒有線程等待,
    if (!readerShouldBlock() &&
// 且讀鎖計數小於最大值,
        r < MAX_COUNT &&
// 且加鎖成功
        compareAndSetState(c, c + SHARED_UNIT)) {
// 讀鎖計數爲0,沒有讀鎖,第一個線程進來
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            firstReaderHoldCount++;
        } else {
// 第三次rh對象不爲空
            HoldCounter rh = cachedHoldCounter;
// rh != null,但tid不同 判斷爲true
            if (rh == null || rh.tid != getThreadId(current))
// 從緩存對象中獲取到緩存對象;存在返回,不存在初始化
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
// 緩存對象計數+1,這時緩存對象中第三個的計數是1
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章