真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

死磕java同步系列之ReentrantReadWriteLock源碼解析

問題

(1)讀寫鎖是什么?

站在用戶的角度思考問題,與客戶深入溝通,找到薊州網(wǎng)站設(shè)計(jì)與薊州網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名與空間、虛擬主機(jī)、企業(yè)郵箱。業(yè)務(wù)覆蓋薊州地區(qū)。

(2)讀寫鎖具有哪些特性?

(3)ReentrantReadWriteLock是怎么實(shí)現(xiàn)讀寫鎖的?

(4)如何使用ReentrantReadWriteLock實(shí)現(xiàn)高效安全的TreeMap?

簡介

讀寫鎖是一種特殊的鎖,它把對(duì)共享資源的訪問分為讀訪問和寫訪問,多個(gè)線程可以同時(shí)對(duì)共享資源進(jìn)行讀訪問,但是同一時(shí)間只能有一個(gè)線程對(duì)共享資源進(jìn)行寫訪問,使用讀寫鎖可以極大地提高并發(fā)量。

特性

讀寫鎖具有以下特性:

是否互斥

可以看到,讀寫鎖除了讀讀不互斥,讀寫、寫讀、寫寫都是互斥的。

那么,ReentrantReadWriteLock是怎么實(shí)現(xiàn)讀寫鎖的呢?

類結(jié)構(gòu)

在看源碼之前,我們還是先來看一下ReentrantReadWriteLock這個(gè)類的主要結(jié)構(gòu)。

死磕 java同步系列之ReentrantReadWriteLock源碼解析

ReentrantReadWriteLock中的類分成三個(gè)部分:

(1)ReentrantReadWriteLock本身實(shí)現(xiàn)了ReadWriteLock接口,這個(gè)接口只提供了兩個(gè)方法readLock()writeLock();

(2)同步器,包含一個(gè)繼承了AQS的Sync內(nèi)部類,以及其兩個(gè)子類FairSync和NonfairSync;

(3)ReadLock和WriteLock兩個(gè)內(nèi)部類實(shí)現(xiàn)了Lock接口,它們具有鎖的一些特性。

源碼分析

主要屬性

// 讀鎖
private final ReentrantReadWriteLock.ReadLock readerLock;
// 寫鎖
private final ReentrantReadWriteLock.WriteLock writerLock;
// 同步器
final Sync sync;

維護(hù)了讀鎖、寫鎖和同步器。

主要構(gòu)造方法

// 默認(rèn)構(gòu)造方法
public ReentrantReadWriteLock() {
    this(false);
}
// 是否使用公平鎖的構(gòu)造方法
public ReentrantReadWriteLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    readerLock = new ReadLock(this);
    writerLock = new WriteLock(this);
}

它提供了兩個(gè)構(gòu)造方法,默認(rèn)構(gòu)造方法使用的是非公平鎖模式,在構(gòu)造方法中初始化了讀鎖和寫鎖。

獲取讀鎖和寫鎖的方法

public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }

屬性中的讀鎖和寫鎖是私有屬性,通過這兩個(gè)方法暴露出去。

下面我們主要分析讀鎖和寫鎖的加鎖、解鎖方法,且都是基于非公平模式的。

ReadLock.lock()

// ReentrantReadWriteLock.ReadLock.lock()
public void lock() {
    sync.acquireShared(1);
}
// AbstractQueuedSynchronizer.acquireShared()
public final void acquireShared(int arg) {
    // 嘗試獲取共享鎖(返回1表示成功,返回-1表示失?。?    if (tryAcquireShared(arg) < 0)
        // 失敗了就可能要排隊(duì)
        doAcquireShared(arg);
}
// ReentrantReadWriteLock.Sync.tryAcquireShared()
protected final int tryAcquireShared(int unused) {
    Thread current = Thread.currentThread();
    // 狀態(tài)變量的值
    // 在讀寫鎖模式下,高16位存儲(chǔ)的是共享鎖(讀鎖)被獲取的次數(shù),低16位存儲(chǔ)的是互斥鎖(寫鎖)被獲取的次數(shù)
    int c = getState();
    // 互斥鎖的次數(shù)
    // 如果其它線程獲得了寫鎖,直接返回-1
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    // 讀鎖被獲取的次數(shù)
    int r = sharedCount(c);

    // 下面說明此時(shí)還沒有寫鎖,嘗試去更新state的值獲取讀鎖
    // 讀者是否需要排隊(duì)(是否是公平模式)
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        // 獲取讀鎖成功
        if (r == 0) {
            // 如果之前還沒有線程獲取讀鎖
            // 記錄第一個(gè)讀者為當(dāng)前線程
            firstReader = current;
            // 第一個(gè)讀者重入的次數(shù)為1
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            // 如果有線程獲取了讀鎖且是當(dāng)前線程是第一個(gè)讀者
            // 則把其重入次數(shù)加1
            firstReaderHoldCount++;
        } else {
            // 如果有線程獲取了讀鎖且當(dāng)前線程不是第一個(gè)讀者
            // 則從緩存中獲取重入次數(shù)保存器
            HoldCounter rh = cachedHoldCounter;
            // 如果緩存不屬性當(dāng)前線程
            // 再從ThreadLocal中獲取
            // readHolds本身是一個(gè)ThreadLocal,里面存儲(chǔ)的是HoldCounter
            if (rh == null || rh.tid != getThreadId(current))
                // get()的時(shí)候會(huì)初始化rh
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                // 如果rh的次數(shù)為0,把它放到ThreadLocal中去
                readHolds.set(rh);
            // 重入的次數(shù)加1(初始次數(shù)為0)
            rh.count++;
        }
        // 獲取讀鎖成功,返回1
        return 1;
    }
    // 通過這個(gè)方法再去嘗試獲取讀鎖(如果之前其它線程獲取了寫鎖,一樣返回-1表示失敗)
    return fullTryAcquireShared(current);
}
// AbstractQueuedSynchronizer.doAcquireShared()
private void doAcquireShared(int arg) {
    // 進(jìn)入AQS的隊(duì)列中
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)
            final Node p = node.predecessor();
            // 如果前一個(gè)節(jié)點(diǎn)是頭節(jié)點(diǎn)(說明是第一個(gè)排隊(duì)的節(jié)點(diǎn))
            if (p == head) {
                // 再次嘗試獲取讀鎖
                int r = tryAcquireShared(arg);
                // 如果成功了
                if (r >= 0) {
                    // 頭節(jié)點(diǎn)后移并傳播
                    // 傳播即喚醒后面連續(xù)的讀節(jié)點(diǎn)
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            // 沒獲取到讀鎖,阻塞并等待被喚醒
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
// AbstractQueuedSynchronizer.setHeadAndPropagate()
private void setHeadAndPropagate(Node node, int propagate) {
    // h為舊的頭節(jié)點(diǎn)
    Node h = head;
    // 設(shè)置當(dāng)前節(jié)點(diǎn)為新頭節(jié)點(diǎn)
    setHead(node);

    // 如果舊的頭節(jié)點(diǎn)或新的頭節(jié)點(diǎn)為空或者其等待狀態(tài)小于0(表示狀態(tài)為SIGNAL/PROPAGATE)
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        // 需要傳播
        // 取下一個(gè)節(jié)點(diǎn)
        Node s = node.next;
        // 如果下一個(gè)節(jié)點(diǎn)為空,或者是需要獲取讀鎖的節(jié)點(diǎn)
        if (s == null || s.isShared())
            // 喚醒下一個(gè)節(jié)點(diǎn)
            doReleaseShared();
    }
}
// AbstractQueuedSynchronizer.doReleaseShared()
// 這個(gè)方法只會(huì)喚醒一個(gè)節(jié)點(diǎn)
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            // 如果頭節(jié)點(diǎn)狀態(tài)為SIGNAL,說明要喚醒下一個(gè)節(jié)點(diǎn)
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                // 喚醒下一個(gè)節(jié)點(diǎn)
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     // 把頭節(jié)點(diǎn)的狀態(tài)改為PROPAGATE成功才會(huì)跳到下面的if
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        // 如果喚醒后head沒變,則跳出循環(huán)
        if (h == head)                   // loop if head changed
            break;
    }
}

看完【死磕 java同步系列之ReentrantLock源碼解析(一)——公平鎖、非公平鎖】的分析再看這章的內(nèi)容應(yīng)該會(huì)比較簡單,中間一樣的方法我們這里直接跳過了。

我們來看看大致的邏輯:

(1)先嘗試獲取讀鎖;

(2)如果成功了直接結(jié)束;

(3)如果失敗了,進(jìn)入doAcquireShared()方法;

(4)doAcquireShared()方法中首先會(huì)生成一個(gè)新節(jié)點(diǎn)并進(jìn)入AQS隊(duì)列中;

(5)如果頭節(jié)點(diǎn)正好是當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn),再次嘗試獲取鎖;

(6)如果成功了,則設(shè)置頭節(jié)點(diǎn)為新節(jié)點(diǎn),并傳播;

(7)傳播即喚醒下一個(gè)讀節(jié)點(diǎn)(如果下一個(gè)節(jié)點(diǎn)是讀節(jié)點(diǎn)的話);

(8)如果頭節(jié)點(diǎn)不是當(dāng)前節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)或者(5)失敗,則阻塞當(dāng)前線程等待被喚醒;

(9)喚醒之后繼續(xù)走(5)的邏輯;

在整個(gè)邏輯中是在哪里連續(xù)喚醒讀節(jié)點(diǎn)的呢?

答案是在doAcquireShared()方法中,在這里一個(gè)節(jié)點(diǎn)A獲取了讀鎖后,會(huì)喚醒下一個(gè)讀節(jié)點(diǎn)B,這時(shí)候B也會(huì)獲取讀鎖,然后B繼續(xù)喚醒C,依次往復(fù),也就是說這里的節(jié)點(diǎn)是一個(gè)喚醒一個(gè)這樣的形式,而不是一個(gè)節(jié)點(diǎn)獲取了讀鎖后一次性喚醒后面所有的讀節(jié)點(diǎn)。

死磕 java同步系列之ReentrantReadWriteLock源碼解析

ReadLock.unlock()

// java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock.unlock
public void unlock() {
    sync.releaseShared(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared
public final boolean releaseShared(int arg) {
    // 如果嘗試釋放成功了,就喚醒下一個(gè)節(jié)點(diǎn)
    if (tryReleaseShared(arg)) {
        // 這個(gè)方法實(shí)際是喚醒下一個(gè)節(jié)點(diǎn)
        doReleaseShared();
        return true;
    }
    return false;
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryReleaseShared
protected final boolean tryReleaseShared(int unused) {
    Thread current = Thread.currentThread();
    if (firstReader == current) {
        // 如果第一個(gè)讀者(讀線程)是當(dāng)前線程
        // 就把它重入的次數(shù)減1
        // 如果減到0了就把第一個(gè)讀者置為空
        if (firstReaderHoldCount == 1)
            firstReader = null;
        else
            firstReaderHoldCount--;
    } else {
        // 如果第一個(gè)讀者不是當(dāng)前線程
        // 一樣地,把它重入的次數(shù)減1
        HoldCounter rh = cachedHoldCounter;
        if (rh == null || rh.tid != getThreadId(current))
            rh = readHolds.get();
        int count = rh.count;
        if (count <= 1) {
            readHolds.remove();
            if (count <= 0)
                throw unmatchedUnlockException();
        }
        --rh.count;
    }
    for (;;) {
        // 共享鎖獲取的次數(shù)減1
        // 如果減為0了說明完全釋放了,才返回true
        int c = getState();
        int nextc = c - SHARED_UNIT;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.doReleaseShared
// 行為跟方法名有點(diǎn)不符,實(shí)際是喚醒下一個(gè)節(jié)點(diǎn)
private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            // 如果頭節(jié)點(diǎn)狀態(tài)為SIGNAL,說明要喚醒下一個(gè)節(jié)點(diǎn)
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                // 喚醒下一個(gè)節(jié)點(diǎn)
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     // 把頭節(jié)點(diǎn)的狀態(tài)改為PROPAGATE成功才會(huì)跳到下面的if
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        // 如果喚醒后head沒變,則跳出循環(huán)
        if (h == head)                   // loop if head changed
            break;
    }
}

解鎖的大致流程如下:

(1)將當(dāng)前線程重入的次數(shù)減1;

(2)將共享鎖總共被獲取的次數(shù)減1;

(3)如果共享鎖獲取的次數(shù)減為0了,說明共享鎖完全釋放了,那就喚醒下一個(gè)節(jié)點(diǎn);

如下圖,ABC三個(gè)節(jié)點(diǎn)各獲取了一次共享鎖,三者釋放的順序分別為ACB,那么最后B釋放共享鎖的時(shí)候tryReleaseShared()才會(huì)返回true,進(jìn)而才會(huì)喚醒下一個(gè)節(jié)點(diǎn)D。

死磕 java同步系列之ReentrantReadWriteLock源碼解析

WriteLock.lock()

// java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock.lock()
public void lock() {
    sync.acquire(1);
}
// java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire()
public final void acquire(int arg) {
    // 先嘗試獲取鎖
    // 如果失敗,則會(huì)進(jìn)入隊(duì)列中排隊(duì),后面的邏輯跟ReentrantLock一模一樣了
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryAcquire()
protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    // 狀態(tài)變量state的值
    int c = getState();
    // 互斥鎖被獲取的次數(shù)
    int w = exclusiveCount(c);
    if (c != 0) {
        // 如果c!=0且w==0,說明共享鎖被獲取的次數(shù)不為0
        // 這句話整個(gè)的意思就是
        // 如果共享鎖被獲取的次數(shù)不為0,或者被其它線程獲取了互斥鎖(寫鎖)
        // 那么就返回false,獲取寫鎖失敗
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        // 溢出檢測(cè)
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // 到這里說明當(dāng)前線程已經(jīng)獲取過寫鎖,這里是重入了,直接把state加1即可
        setState(c + acquires);
        // 獲取寫鎖成功
        return true;
    }
    // 如果c等于0,就嘗試更新state的值(非公平模式writerShouldBlock()返回false)
    // 如果失敗了,說明獲取寫鎖失敗,返回false
    // 如果成功了,說明獲取寫鎖成功,把自己設(shè)置為占有者,并返回true
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}
// 獲取寫鎖失敗了后面的邏輯跟ReentrantLock是一致的,進(jìn)入隊(duì)列排隊(duì),這里就不列源碼了

寫鎖獲取的過程大致如下:

(1)嘗試獲取鎖;

(2)如果有讀者占有著讀鎖,嘗試獲取寫鎖失??;

(3)如果有其它線程占有著寫鎖,嘗試獲取寫鎖失??;

(4)如果是當(dāng)前線程占有著寫鎖,嘗試獲取寫鎖成功,state值加1;

(5)如果沒有線程占有著鎖(state==0),當(dāng)前線程嘗試更新state的值,成功了表示嘗試獲取鎖成功,否則失??;

(6)嘗試獲取鎖失敗以后,進(jìn)入隊(duì)列排隊(duì),等待被喚醒;

(7)后續(xù)邏輯跟ReentrantLock是一致;

WriteLock.unlock()

// java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock.unlock()
public void unlock() {
    sync.release(1);
}
//java.util.concurrent.locks.AbstractQueuedSynchronizer.release()
public final boolean release(int arg) {
    // 如果嘗試釋放鎖成功(完全釋放鎖)
    // 就嘗試喚醒下一個(gè)節(jié)點(diǎn)
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
// java.util.concurrent.locks.ReentrantReadWriteLock.Sync.tryRelease()
protected final boolean tryRelease(int releases) {
    // 如果寫鎖不是當(dāng)前線程占有著,拋出異常
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    // 狀態(tài)變量的值減1
    int nextc = getState() - releases;
    // 是否完全釋放鎖
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
        setExclusiveOwnerThread(null);
    // 設(shè)置狀態(tài)變量的值
    setState(nextc);
    // 如果完全釋放了寫鎖,返回true
    return free;
}

寫鎖釋放的過程大致為:

(1)先嘗試釋放鎖,即狀態(tài)變量state的值減1;

(2)如果減為0了,說明完全釋放了鎖;

(3)完全釋放了鎖才喚醒下一個(gè)等待的節(jié)點(diǎn);

總結(jié)

(1)ReentrantReadWriteLock采用讀寫鎖的思想,能提高并發(fā)的吞吐量;

(2)讀鎖使用的是共享鎖,多個(gè)讀鎖可以一起獲取鎖,互相不會(huì)影響,即讀讀不互斥;

(3)讀寫、寫讀和寫寫是會(huì)互斥的,前者占有著鎖,后者需要進(jìn)入AQS隊(duì)列中排隊(duì);

(4)多個(gè)連續(xù)的讀線程是一個(gè)接著一個(gè)被喚醒的,而不是一次性喚醒所有讀線程;

(5)只有多個(gè)讀鎖都完全釋放了才會(huì)喚醒下一個(gè)寫線程;

(6)只有寫鎖完全釋放了才會(huì)喚醒下一個(gè)等待者,這個(gè)等待者有可能是讀線程,也可能是寫線程;

彩蛋

(1)如果同一個(gè)線程先獲取讀鎖,再獲取寫鎖會(huì)怎樣?

死磕 java同步系列之ReentrantReadWriteLock源碼解析

分析上圖中的代碼,在tryAcquire()方法中,如果讀鎖被獲取的次數(shù)不為0(c != 0 && w == 0),返回false,返回之后外層方法會(huì)讓當(dāng)前線程阻塞。

可以通過下面的方法驗(yàn)證:

readLock.lock();
writeLock.lock();
writeLock.unlock();
readLock.unlock();

運(yùn)行程序后會(huì)發(fā)現(xiàn)代碼停止在writeLock.lock();,當(dāng)然,你也可以打個(gè)斷點(diǎn)跟蹤進(jìn)去看看。

(2)如果同一個(gè)線程先獲取寫鎖,再獲取讀鎖會(huì)怎樣?

死磕 java同步系列之ReentrantReadWriteLock源碼解析

分析上面的代碼,在tryAcquireShared()方法中,第一個(gè)紅框處并不會(huì)返回,因?yàn)椴粷M足getExclusiveOwnerThread() != current;第二個(gè)紅框處如果原子更新成功就說明獲取了讀鎖,然后就會(huì)執(zhí)行第三個(gè)紅框處的代碼把其重入次數(shù)更改為1。

可以通過下面的方法驗(yàn)證:

writeLock.lock();
readLock.lock();
readLock.unlock();
writeLock.unlock();

你可以打個(gè)斷點(diǎn)跟蹤一下看看。

(3)死鎖了么?

通過上面的兩個(gè)例子,我們可以感受到同一個(gè)線程先讀后寫和先寫后讀是完全不一樣的,為什么不一樣呢?

先讀后寫,一個(gè)線程占有讀鎖后,其它線程還是可以占有讀鎖的,這時(shí)候如果在其它線程占有讀鎖之前讓自己占有了寫鎖,其它線程又不能占有讀鎖了,這段程序會(huì)非常難實(shí)現(xiàn),邏輯也很奇怪,所以,設(shè)計(jì)成只要一個(gè)線程占有了讀鎖,其它線程包括它自己都不能再獲取寫鎖。

先寫后讀,一個(gè)線程占有寫鎖后,其它線程是不能占有任何鎖的,這時(shí)候,即使自己占有一個(gè)讀鎖,對(duì)程序的邏輯也不會(huì)有任何影響,所以,一個(gè)線程占有寫鎖后是可以再占有讀鎖的,只是這個(gè)時(shí)候其它線程依然無法獲取讀鎖。

如果你仔細(xì)思考上面的邏輯,你會(huì)發(fā)現(xiàn)一個(gè)線程先占有讀鎖后占有寫鎖,會(huì)有一個(gè)很大的問題——鎖無法被釋放也無法被獲取了。這個(gè)線程先占有了讀鎖,然后自己再占有寫鎖的時(shí)候會(huì)阻塞,然后它就自己把自己搞死了,進(jìn)而把其它線程也搞死了,它無法釋放鎖,其它線程也無法獲得鎖了。

這是死鎖嗎?似乎不是,死鎖的定義是線程A占有著線程B需要的資源,線程B占有著線程A需要的資源,兩個(gè)線程相互等待對(duì)方釋放資源,經(jīng)典的死鎖例子如下:

Object a = new Object();
Object b = new Object();

new Thread(()->{
    synchronized (a) {
        LockSupport.parkNanos(1000000);
        synchronized (b) {

        }
    }
}).start();

new Thread(()->{
    synchronized (b) {
        synchronized (a) {

        }
    }
}).start();

簡單的死鎖用jstack是可以看到的:

"Thread-1":
        at com.coolcoding.code.synchronize.ReentrantReadWriteLockTest.lambda$main$1(ReentrantReadWriteLockTest.java:40)
        - waiting to lock <0x000000076baa9068> (a java.lang.Object)
        - locked <0x000000076baa9078> (a java.lang.Object)
        at com.coolcoding.code.synchronize.ReentrantReadWriteLockTest$$Lambda$2/1831932724.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0":
        at com.coolcoding.code.synchronize.ReentrantReadWriteLockTest.lambda$main$0(ReentrantReadWriteLockTest.java:32)
        - waiting to lock <0x000000076baa9078> (a java.lang.Object)
        - locked <0x000000076baa9068> (a java.lang.Object)
        at com.coolcoding.code.synchronize.ReentrantReadWriteLockTest$$Lambda$1/1096979270.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

(4)如何使用ReentrantReadWriteLock實(shí)現(xiàn)一個(gè)高效安全的TreeMap?

class SafeTreeMap {
    private final Map m = new TreeMap();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public Object get(String key) {
        readLock.lock();
        try {
            return m.get(key);
        } finally {
            readLock.unlock();
        }
    }

    public Object put(String key, Object value) {
        writeLock.lock();
        try {
            return m.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }
}

推薦閱讀

  1. 死磕 java同步系列之ReentrantLock VS synchronized

  2. 死磕 java同步系列之ReentrantLock源碼解析(二)——條件鎖

  3. 死磕 java同步系列之ReentrantLock源碼解析(一)——公平鎖、非公平鎖

  4. 死磕 java同步系列之AQS起篇

  5. 死磕 java同步系列之自己動(dòng)手寫一個(gè)鎖Lock

  6. 死磕 java魔法類之Unsafe解析

  7. 死磕 java同步系列之JMM(Java Memory Model)

  8. 死磕 java同步系列之volatile解析

  9. 死磕 java同步系列之synchronized解析

歡迎關(guān)注我的公眾號(hào)“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。

死磕 java同步系列之ReentrantReadWriteLock源碼解析


網(wǎng)站題目:死磕java同步系列之ReentrantReadWriteLock源碼解析
轉(zhuǎn)載注明:http://weahome.cn/article/pdepge.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部