成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ReentrantLock 實現(xiàn)原理筆記(一)

Fourierr / 3924人閱讀

摘要:中節(jié)點的狀態(tài)機流轉(zhuǎn)機制是的核心為一系列同步器依賴于一個多帶帶的原子變量的同步器提供了一個非常有用的基礎(chǔ)。所有的同步機制的實現(xiàn)均依賴于對改變量的原子操作。

java.util.concurrent.locks.ReentrantLock

exclusive : adj. (個人或集體) 專用的,專有的,獨有的,獨占的; 排外的; 不愿接收新成員(尤指較低社會階層)的; 高檔的; 豪華的; 高級的

reentrant : 可重入; 可重入的; 重入; 可再入的; 重進入

一切從 Thread 線程開始

獨占線程 exclusiveOwnerThread 出場:

package java.util.concurrent.locks;

/**
 * A synchronizer that may be exclusively owned by a thread.  This
 * class provides a basis for creating locks and related synchronizers
 * that may entail a notion of ownership.  The
 * {@code AbstractOwnableSynchronizer} class itself does not manage or
 * use this information. However, subclasses and tools may use
 * appropriately maintained values to help control and monitor access
 * and provide diagnostics.
 *
 * @since 1.6
 * @author Doug Lea
 */
public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;

    /**
     * Empty constructor for use by subclasses.
     */
    protected AbstractOwnableSynchronizer() { }

    /**
     * The current owner of exclusive mode synchronization.
     */
    private transient Thread exclusiveOwnerThread;

    /**
     * Sets the thread that currently owns exclusive access.
     * A {@code null} argument indicates that no thread owns access.
     * This method does not otherwise impose any synchronization or
     * {@code volatile} field accesses.
     * @param thread the owner thread
     */
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    /**
     * Returns the thread last set by {@code setExclusiveOwnerThread},
     * or {@code null} if never set.  This method does not otherwise
     * impose any synchronization or {@code volatile} field accesses.
     * @return the owner thread
     */
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

這里的獲取當前鎖的獨占線程的方法是 final 的:

protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }

AQS 繼承這個類:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer {}
快速認識 ReentrantLock

直接上代碼:

fun main() {

    val time1 = measureTimeMillis {
        singleThreadSum()
    }

    val time2 = measureTimeMillis {
        multiThreadSumNoLock()
    }

    val time3 = measureTimeMillis {
        multiThreadSumUseLock()
    }

    println("time1:$time1")
    println("time2:$time2")
    println("time3:$time3")
}


fun singleThreadSum() {
    var sum: Long = 0

    for (i in 1..100000) {
        sum += i
    }

    for (i in 100001..200000) {
        sum += i
    }

    println("singleThreadSum: $sum")
}


fun multiThreadSumNoLock() {
    var sum: Long = 0

    val t1 = Thread {
        for (i in 1..100000) {
            sum += i
        }
    }

    val t2 = Thread {
        for (i in 100001..200000) {
            sum += i
        }
    }

    t1.start()
    t2.start()
    t1.join()
    t2.join()


    println("multiThreadSumNoLock:$sum")
}

fun multiThreadSumUseLock() {
    var sum: Long = 0
    val lock = ReentrantLock()

    val t1 = Thread {
        lock.lock()
        try {
            for (i in 1..100000) {
                sum += i
            }
        } finally {
            lock.unlock()
        }
    }

    val t2 = Thread {
        lock.lock()
        try {
            for (i in 100001..200000) {
                sum += i
            }
        } finally {
            lock.unlock()
        }
    }

    t1.start()
    t2.start()
    t1.join()
    t2.join()

    println("multiThreadSumUseLock:$sum")
}

運行結(jié)果(每次會有不同):

singleThreadSum: 20000100000
multiThreadSumNoLock:19496951532
multiThreadSumUseLock:20000100000
time1:2
time2:11
time3:8

其中, lock() 方法背后發(fā)生的事情大概如下圖:

lock()

如果沒有線程使用則立即返回,并設(shè)置state為1;
如果當前線程已經(jīng)占有鎖,則state加1;如果其他線程占有鎖,則當前線程不可用,等待.

tryLock()

如果鎖可用,則獲取鎖,并立即返回值 true。
如果鎖不可用,則此方法將立即返回值 false.

unlock()

嘗試釋放鎖,如果當前線程占有鎖則count減一,如果count為0則釋放鎖。
如果占有線程不是當前線程,則拋異常.

ReentrantLock 是什么?

ReentrantLock, 可重入鎖 , 是一個基于AQS( )并發(fā)框架的并發(fā)控制類.

ReentrantLock內(nèi)部實現(xiàn)了3個類,分別是:

Sync
NoFairSync
FairSync

其中Sync繼承自AQS,實現(xiàn)了釋放鎖的模板方法tryRelease(int).

   abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        Sync() {
        }

        abstract void lock();

        final boolean nonfairTryAcquire(int var1) {
            Thread var2 = Thread.currentThread();
            int var3 = this.getState();
            if (var3 == 0) {
                if (this.compareAndSetState(0, var1)) {
                    this.setExclusiveOwnerThread(var2);
                    return true;
                }
            } else if (var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;
                if (var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(var4);
                return true;
            }

            return false;
        }

        protected final boolean tryRelease(int var1) {
            int var2 = this.getState() - var1;
            if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
                throw new IllegalMonitorStateException();
            } else {
                boolean var3 = false;
                if (var2 == 0) {
                    var3 = true;
                    this.setExclusiveOwnerThread((Thread)null);
                }

                this.setState(var2);
                return var3;
            }
        }

        protected final boolean isHeldExclusively() {
            return this.getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject(this);
        }

        final Thread getOwner() {
            return this.getState() == 0 ? null : this.getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return this.isHeldExclusively() ? this.getState() : 0;
        }

        final boolean isLocked() {
            return this.getState() != 0;
        }

        private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
            var1.defaultReadObject();
            this.setState(0);
        }
    }
}

而NoFairSync和FairSync都繼承自Sync,實現(xiàn)各種獲取鎖的方法tryAcquire(int)。

FairSync
   static final class FairSync extends ReentrantLock.Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        FairSync() {
        }

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

        protected final boolean tryAcquire(int var1) {
            Thread var2 = Thread.currentThread();
            int var3 = this.getState();
            if (var3 == 0) {
                if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, var1)) {
                    this.setExclusiveOwnerThread(var2);
                    return true;
                }
            } else if (var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;
                if (var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(var4);
                return true;
            }

            return false;
        }
    }
NonfairSync
    static final class NonfairSync extends ReentrantLock.Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        NonfairSync() {
        }

        final void lock() {
            if (this.compareAndSetState(0, 1)) {
                this.setExclusiveOwnerThread(Thread.currentThread());
            } else {
                this.acquire(1);
            }

        }

        protected final boolean tryAcquire(int var1) {
            return this.nonfairTryAcquire(var1);
        }
    }

[Ref: https://www.jianshu.com/p/620... ]

Lock

First time a thread wants to acquire the lock, it acquires it without any wait as the lock is not owned by any thread yet.?Thread acquires and owns it.

Lock below is called the?ReentrantLock, which is either open to be acquired or locked and is determined by its state which is either zero or non-zero. It knows the owning thread.

What is Reentrance Mutext?

When a thread acquires a previously unheld lock, the JVM records the owner thread and sets the acquisition count to one. If that same thread acquires the lock again, the count is incremented, and when the owning thread calls unlock, the count is decremented. When the count reaches zero, the lock is released.

Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis. In other words, if a thread is not reentrant, and?tries to acquire a lock that it already holds, the request won’t succeed.

A re-entrant lock can follow either fair or non-fair policy, by default it is non-fair. In this article we will discuss both non-fair and fair locks. Below is the class diagram.

Class diagram

ReentrantLock 實現(xiàn)原理 獲取鎖的背后發(fā)生了什么?

When the second thread fails to acquire lock as the lock is already acquired by the first thread then it adds itself to the waiting node linked list at the end of the list.

Try acquiring lock for the queued thread:

After the thread adds itself to the waiting node linked list, it will still try acquiring lock in exclusive uninterruptible mode.

If the thread trying to acquire lock is not the successor to the header node then it won’t be able to acquire the lock. It will still not be parked yet and the predecessor’s status field will be checked to figure out whether the status represents the correct state.

Thread Parking

If the lock to acquire is currently owned by some other thread then the current thread trying to acquire the lock will be parked, that is, disabled for thread scheduling purposes unless the permit is available. If the permit is available then it is consumed and the call returns immediately, otherwise it remains blocked. The blocker (the lock) which is responsible for this thread parking will be set to the thread.

Data Structure

If the lock is already owned by a thread, any other thread trying to acquire the lock will put on a hold and will be waiting in a queue. The thread would be disabled for thread scheduling purpose. The queue here is a double linked list. The lock will know the owning thread, state (0 or 1), head and tail of the linked list. If the node’ successor node is waiting for the lock then its wait state property (-1) will reflect that.

Waiting Queue: 雙向鏈表

The thread fails to acquire the lock fails as the lock is already acquired by another thread. The thread is added to a double linked list queue where the header is a dummy node with a mode indicating that it is waiting for a signal and the current thread which failed to acquire the lock becomes the tail node and is the next node to header. Since the current thread fails to get the lock, it is parked as it remains blocked till some other thread unblocks it.

Second thread tries to acquire the lock, it too fails so gets queued. It becomes the new tail and the thread is parked.

unlock() 算法

If the current thread is the holder of this lock then the hold count is decremented. If the hold count is now zero then the lock is released. If the current thread is not the holder of this lock then llegalMonitorStateException is thrown.

Once the lock is released, the lock finds out the node which is in waiting status, removed the waiting status and wakes up the node’s successor so that the node woken up can acquire the lock.

    /**
     * Wakes up node"s successor, if one exists.
     *
     * @param node the node
     */
    private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

The thread to unpark is held in successor, which is normally just the next node. But if cancelled or apparently null, traverse backwards from tail to find the actual non-cancelled successor.

If the node woken up is immediate successor to the head node, it tries to acquire the lock and become the new head. The old head and the variables referring to it are nullified so the gc can claim the memory.

If the owning thread releases the lock, the head’s successor node’s thread will be unparked. The successor node is tail in this case, so we will end up with a single node which is itself head and tail both. The unparked thread will take the ownership of the lock.

Further release of the lock will simply nullify the owning thread and the state of the lock will be set to 0.

公平鎖和非公平鎖

公平鎖是指多個線程按照申請鎖的順序來獲取鎖,線程直接進入FIFO隊列,隊列中的第一個線程才能獲得鎖。

用一個打水的例子來理解:

公平鎖的優(yōu)點是等待鎖的線程不會夯死。缺點是吞吐效率相對非公平鎖要低,等待隊列中除第一個線程以外的所有線程都會阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。

非公平鎖是多個線程加鎖時直接嘗試獲取鎖,獲取不到才會到等待隊列的隊尾等待。但如果此時鎖剛好可用,那么這個線程可以無需阻塞直接獲取到鎖,所以非公平鎖有可能出現(xiàn)后申請鎖的線程先獲取鎖的場景。

非公平鎖的優(yōu)點是可以減少喚起線程的開銷(因為可能有的線程可以直接獲取到鎖,CPU也就不用喚醒它),所以整體的吞吐效率高。缺點是處于等待隊列中的線程可能會夯死(試想恰好每次有新線程來,它恰巧都每次獲取到鎖,此時還在排隊等待獲取鎖的線程就悲劇了.....),或者等很久才會獲得鎖。

小結(jié): 公平鎖和非公平鎖的差異在于是否按照申請鎖的順序來獲取鎖,非公平鎖可能會出現(xiàn)有多個線程等待時,有一個人品特別的好的線程直接沒有等待而直接獲取到了鎖的情況,他們各有利弊;Reentrantlock在構(gòu)造時默認是非公平的,可以通過參數(shù)控制。

[ Ref:https://www.jianshu.com/p/620... ]

Reentrantlock 與 AQS

java.util.concurrent.locks.AbstractQueuedSynchronizer

 * Provides a framework for implementing blocking locks and related
 * synchronizers (semaphores, events, etc) that rely on
 * first-in-first-out (FIFO) wait queues.  This class is designed to
 * be a useful basis for most kinds of synchronizers ....

ref: [http://www.cs.rochester.edu/wcms/research/systems/high_performance_synch/ ]

AQS是AbustactQueuedSynchronizer的簡稱,它是一個Java提高的底層同步工具類,用一個int類型的變量表示同步狀態(tài),并提供了一系列的CAS操作來管理這個同步狀態(tài)。AQS的主要作用是為Java中的并發(fā)同步組件提供統(tǒng)一的底層支持.

同步工具類Semaphore、CountDownLatch、ReentrantLock、ReentrantReadWriteLock、FutureTask等雖然各自都有不同特征,但是簡單看一下源碼,每個類內(nèi)部都包含一個如下的內(nèi)部子類定義:

abstract static class Sync extends AbstractQueuedSynchronizer

[ ref:https://blog.csdn.net/zhangdo... ]

AQS提供了一種實現(xiàn)阻塞鎖和一系列依賴FIFO等待隊列的同步器的框架,如下圖所示。

同步隊列是AQS很重要的組成部分,它是一個雙端隊列,遵循FIFO原則,主要作用是用來存放在鎖上阻塞的線程,當一個線程嘗試獲取鎖時,如果已經(jīng)被占用,那么當前線程就會被構(gòu)造成一個Node節(jié)點 add 到同步隊列的尾部,隊列的頭節(jié)點是成功獲取鎖的節(jié)點,當頭節(jié)點線程是否鎖時,會喚醒后面的節(jié)點并釋放當前頭節(jié)點的引用。

Queue 中節(jié)點 Node 的狀態(tài)機流轉(zhuǎn)機制是 AQS 的核心:

    static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;

        /** waitStatus value to indicate thread has cancelled */
        static final int CANCELLED =  1;
        /** waitStatus value to indicate successor"s thread needs unparking */
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition */
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate
         */
        static final int PROPAGATE = -3;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn"t need to
         * signal. So, most code doesn"t need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        volatile int waitStatus;

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * head only as a result of successful acquire. A
         * cancelled thread never succeeds in acquiring, and a thread only
         * cancels itself, not any other node.
         */
         .......
}

AQS為一系列同步器依賴于一個多帶帶的原子變量(state)的同步器提供了一個非常有用的基礎(chǔ)。子類們必須定義改變state變量的protected方法,這些方法定義了state是如何被獲取或釋放的。鑒于此,本類中的其他方法執(zhí)行所有的排隊和阻塞機制。子類也可以維護其他的state變量,但是為了保證同步,必須原子地操作這些變量。

?? AbstractQueuedSynchronizer中對state的操作是原子的,且不能被繼承。所有的同步機制的實現(xiàn)均依賴于對改變量的原子操作。為了實現(xiàn)不同的同步機制,我們需要創(chuàng)建一個非共有的(non-public internal)擴展了AQS類的內(nèi)部輔助類來實現(xiàn)相應(yīng)的同步邏輯。AbstractQueuedSynchronizer并不實現(xiàn)任何同步接口,它提供了一些可以被具體實現(xiàn)類直接調(diào)用的一些原子操作方法來重寫相應(yīng)的同步邏輯。AQS同時提供了互斥模式(exclusive)和共享模式(shared)兩種不同的同步邏輯。一般情況下,子類只需要根據(jù)需求實現(xiàn)其中一種模式,當然也有同時實現(xiàn)兩種模式的同步類,如ReadWriteLock。接下來將詳細介紹AbstractQueuedSynchronizer的提供的一些具體實現(xiàn)方法。

state狀態(tài)

??AbstractQueuedSynchronizer維護了一個volatile int類型的變量,用戶表示當前同步狀態(tài)。volatile雖然不能保證操作的原子性,但是保證了當前變量state的可見性。至于volatile的具體語義,可以參考相關(guān)文章。state的訪問方式有三種:

getState()

setState()

compareAndSetState()

??這三種叫做均是原子操作,其中compareAndSetState的實現(xiàn)依賴于Unsafe的compareAndSwapInt()方法。代碼實現(xiàn)如下:

    /**
     * The synchronization state.
     */
    private volatile int state;

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
自定義資源共享方式

??AQS定義兩種資源共享方式:Exclusive(獨占,只有一個線程能執(zhí)行,如ReentrantLock)和Share(共享,多個線程可同時執(zhí)行,如Semaphore/CountDownLatch)。
??不同的自定義同步器爭用共享資源的方式也不同。自定義同步器在實現(xiàn)時只需要實現(xiàn)共享資源state的獲取與釋放方式即可,至于具體線程等待隊列的維護(如獲取資源失敗入隊/喚醒出隊等),AQS已經(jīng)在頂層實現(xiàn)好了。自定義同步器實現(xiàn)時主要實現(xiàn)以下幾種方法:

isHeldExclusively():該線程是否正在獨占資源。只有用到condition才需要去實現(xiàn)它。

tryAcquire(int):獨占方式。嘗試獲取資源,成功則返回true,失敗則返回false。

tryRelease(int):獨占方式。嘗試釋放資源,成功則返回true,失敗則返回false。

tryAcquireShared(int):共享方式。嘗試獲取資源。負數(shù)表示失敗;0表示成功,但沒有剩余可用資源;正數(shù)表示成功,且有剩余資源。

tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點返回true,否則返回false。

[ Ref:https://www.jianshu.com/p/da9... ]

ReentrantLock 重入鎖的基本原理是判斷上次獲取鎖的線程是否為當前線程,如果是則可再次進入臨界區(qū),如果不是,則阻塞。

由于ReentrantLock是基于AQS實現(xiàn)的,底層通過操作同步狀態(tài)來獲取鎖.,下面看一下非公平鎖的實現(xiàn)邏輯:

final boolean nonfairTryAcquire(int acquires) {
            //獲取當前線程
            final Thread current = Thread.currentThread();
            //通過AQS獲取同步狀態(tài)
            int c = getState();
            //同步狀態(tài)為0,說明臨界區(qū)處于無鎖狀態(tài),
            if (c == 0) {
                //修改同步狀態(tài),即加鎖
                if (compareAndSetState(0, acquires)) {
                    //將當前線程設(shè)置為鎖的owner
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果臨界區(qū)處于鎖定狀態(tài),且上次獲取鎖的線程為當前線程
            else if (current == getExclusiveOwnerThread()) {
                 //則遞增同步狀態(tài)
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
讀寫鎖

Java提供了一個基于AQS到讀寫鎖實現(xiàn)ReentrantReadWriteLock,該讀寫鎖到實現(xiàn)原理是:將同步變量state按照高16位和低16位進行拆分,高16位表示讀鎖,低16位表示寫鎖。

寫鎖的獲取與釋放

寫鎖是一個獨占鎖,所以我們看一下ReentrantReadWriteLock中tryAcquire(arg)的實現(xiàn):

protected final boolean tryAcquire(int acquires) {
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
            if (c != 0) {
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                setState(c + acquires);
                return true;
            }
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            setExclusiveOwnerThread(current);
            return true;
        }

上述代碼的處理流程已經(jīng)非常清晰:

獲取同步狀態(tài),并從中分離出低16為的寫鎖狀態(tài)
如果同步狀態(tài)不為0,說明存在讀鎖或?qū)戞i
如果存在讀鎖(c !=0 && w == 0),則不能獲取寫鎖(保證寫對讀的可見性)
如果當前線程不是上次獲取寫鎖的線程,則不能獲取寫鎖(寫鎖為獨占鎖)
如果以上判斷均通過,則在低16為寫鎖同步狀態(tài)上利用CAS進行修改(增加寫鎖同步狀態(tài),實現(xiàn)可重入)
將當前線程設(shè)置為寫鎖的獲取線程

寫鎖的釋放過程與獨占鎖基本相同:

protected final boolean tryRelease(int releases) {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            int nextc = getState() - releases;
            boolean free = exclusiveCount(nextc) == 0;
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }

在釋放的過程中,不斷減少讀鎖同步狀態(tài),只為同步狀態(tài)為0時,寫鎖完全釋放。

讀鎖的獲取與釋放

讀鎖是一個共享鎖,獲取讀鎖的步驟如下:

獲取當前同步狀態(tài)
計算高16為讀鎖狀態(tài)+1后的值
如果大于能夠獲取到的讀鎖的最大值,則拋出異常
如果存在寫鎖并且當前線程不是寫鎖的獲取者,則獲取讀鎖失敗
如果上述判斷都通過,則利用CAS重新設(shè)置讀鎖的同步狀態(tài)
讀鎖的獲取步驟與寫鎖類似,即不斷的釋放寫鎖狀態(tài),直到為0時,表示沒有線程獲取讀鎖。

[ ref:https://blog.csdn.net/zhangdo... ]

Unsafe 類

我們可以看到在 AbstractQueuedSynchronizer 中用到了 Unsafe 類:

    /**
     * Setup to support compareAndSet. We need to natively implement
     * this here: For the sake of permitting future enhancements, we
     * cannot explicitly subclass AtomicInteger, which would be
     * efficient and useful otherwise. So, as the lesser of evils, we
     * natively implement using hotspot intrinsics API. And while we
     * are at it, we do the same for other CASable fields (which could
     * otherwise be done with atomic field updaters).
     */
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }

    /**
     * CAS head field. Used only by enq.
     */
    private final boolean compareAndSetHead(Node update) {
        return unsafe.compareAndSwapObject(this, headOffset, null, update);
    }

    /**
     * CAS tail field. Used only by enq.
     */
    private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }

    /**
     * CAS waitStatus field of a node.
     */
    private static final boolean compareAndSetWaitStatus(Node node,
                                                         int expect,
                                                         int update) {
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                        expect, update);
    }

    /**
     * CAS next field of a node.
     */
    private static final boolean compareAndSetNext(Node node,
                                                   Node expect,
                                                   Node update) {
        return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
    }
}

實現(xiàn)線程安全且高效地設(shè)置隊列中節(jié)點 Node 的等待狀態(tài) waitStatus .

比如說, ReentrantLock 中非公平鎖的實現(xiàn)代碼中:

    /**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

compareAndSetState(0, 1) 方法:

    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

Unsafe 類里面的實現(xiàn)方法基本都是 native 方法:

...
    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);

    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
...
參考資料

https://www.javarticles.com/2012/09/reentrant-lock.html

https://www.javarticles.com/2016/06/java-reentrantlock-interruption-example.html

Kotlin 開發(fā)者社區(qū)

國內(nèi)第一Kotlin 開發(fā)者社區(qū)公眾號,主要分享、交流 Kotlin 編程語言、Spring Boot、Android、React.js/Node.js、函數(shù)式編程、編程思想等相關(guān)主題。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/75300.html

相關(guān)文章

  • 后臺開發(fā)常問面試題集錦(問題搬運工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識點總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機制解讀抽象類與三大特征時間和時間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    spacewander 評論0 收藏0
  • 后臺開發(fā)常問面試題集錦(問題搬運工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識點總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機制解讀抽象類與三大特征時間和時間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    xfee 評論0 收藏0
  • 后臺開發(fā)常問面試題集錦(問題搬運工,附鏈接)

    摘要:基礎(chǔ)問題的的性能及原理之區(qū)別詳解備忘筆記深入理解流水線抽象關(guān)鍵字修飾符知識點總結(jié)必看篇中的關(guān)鍵字解析回調(diào)機制解讀抽象類與三大特征時間和時間戳的相互轉(zhuǎn)換為什么要使用內(nèi)部類對象鎖和類鎖的區(qū)別,,優(yōu)缺點及比較提高篇八詳解內(nèi)部類單例模式和 Java基礎(chǔ)問題 String的+的性能及原理 java之yield(),sleep(),wait()區(qū)別詳解-備忘筆記 深入理解Java Stream流水...

    makeFoxPlay 評論0 收藏0
  • Synchronize和ReentrantLock區(qū)別

    摘要:的鎖是非公平鎖,默認情況下也是非公平鎖,但可以通過帶布爾值的構(gòu)造函數(shù)要求使用公平鎖。有序性,是保證線程內(nèi)串行語義,避免指令重排等。公平性是減少線程饑餓個別線程長期等待鎖,但始終無法獲取情況發(fā)生的一個辦法。 目錄介紹 1.Synchronize和ReentrantLock區(qū)別 1.1 相似點 1.2 區(qū)別 1.3 什么是線程安全問題?如何理解 1.4 線程安全需要保證幾個基本特性 ...

    FuisonDesign 評論0 收藏0

發(fā)表評論

0條評論

Fourierr

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<