Java鎖之CountDownLatch、Atomic源碼解析

1.CountDownLatch


1.1整體架構

  • 一個線程或多個線程等待所有線程運行完畢,在繼續執行
  • sync 是一個同步器,是 CountDownLatch 的內部類實現
private static final class Sync extends AbstractQueuedSynchronizer {...}

1.2await方法

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // state爲0的時候不會進入判斷,就會退出
        if (tryAcquireShared(arg) < 0)// 嘗試獲取共享鎖,sync子類實現的方法
            doAcquireSharedInterruptibly(arg);// AQS實現的方法,會進行阻塞
    }

    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    // state爲0的時候才能滿足條件,否則就阻塞
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);//設置頭並且喚醒後置節點
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) && // 通過pre判斷node是否需要阻塞!
                    parkAndCheckInterrupt()) // 進行加鎖阻塞!!!!
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

主要實現是調用的AQS實現的獲取共享鎖方法,

先將節點添加到尾部,然後就一直自旋直到前驅節點是頭節點,就設置頭並且喚醒後直接點

// 帶有超時時間的,最終都會轉化成毫秒
public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

1.3countDown

  • 每調用一次,都會使 state 減一,底層調用的方法如下:
public void countDown() {
    sync.releaseShared(1);
}

// AQS實現的方法
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { // 釋放節點,cas改變狀態,CountDownLatch的子類sync實現的方法
        doReleaseShared(); // 喚醒節點,LockSupport.unpark(s.thread);釋放鎖,AQS實現的方法
        return true;
    }
    return false;
}

// CountDownLatch的子類sync實現的方法
protected boolean tryReleaseShared(int releases) {
    // 自旋保證 CAS 一定可以成功
    for (;;) {
        int c = getState();
        // state 已經是 0 了,直接返回 false
        if (c == 0)
            return false;
        // 對 state 進行遞減
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {// 前繼節點爲此狀態,代表後繼節點正在阻塞
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;           
                unparkSuccessor(h); // 實際進行解鎖,進行喚醒
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;        
        }
        // 前面的cas操作都成功就說明釋放節點成功,退出即可
        if (h == head)         
            break;
    }
}

第一步:釋放節點,cas改變節點狀態

第二步:喚醒節點,釋放掉鎖


2.Atomic


2.1整體架構

  • Atomic 打頭的原子操作類,在高併發場景下,都是線程安全的
private volatile int value;

// 初始化
public AtomicInteger(int initialValue) {
    value = initialValue;
}
// 得到當前值
public final int get() {
    return value;
}
// 自增 1,並返回自增之前的值    
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
// 自減 1,並返回自增之前的值    
public final int getAndDecrement() {
    return unsafe.getAndAddInt(this, valueOffset, -1);
}

 

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