1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Java ReentrantLock 源碼解析與簡(jiǎn)單使用

        共 31476字,需瀏覽 63分鐘

         ·

        2021-04-25 10:03

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        ReentrantLock是juc中的一個(gè)可重入鎖獨(dú)占鎖,該鎖分為公平鎖和非公平鎖。獨(dú)占鎖是這個(gè)鎖一旦被一個(gè)線程占據(jù)其他線程就不能搶占,可重入鎖是如果這個(gè)線程重復(fù)獲取這個(gè)鎖不需要進(jìn)行等待,直接進(jìn)入。公平鎖和非公平鎖是剛進(jìn)入的線程是否可以和在隊(duì)列中等待的第一個(gè)線程進(jìn)行競(jìng)爭(zhēng),如果是非公平鎖則剛進(jìn)入的線程在當(dāng)前鎖空閑的時(shí)候可以和等待隊(duì)列中的第一個(gè)線程進(jìn)行競(jìng)爭(zhēng),如果是公平鎖則不能,剛進(jìn)入的線程必須進(jìn)入隊(duì)列等待。


        一、java.util.concurrent.locks.Lock接口

        public class ReentrantLock implements Lock, java.io.Serializable {
        .....
        }

        從源碼中我們可以看到ReentrantLock 實(shí)現(xiàn)了Lock接口。Lock接口是juc中定義的一個(gè)鎖的接口。

        public interface Lock {

            // 獲取鎖
            void lock();

            // 獲取鎖的過(guò)程能響應(yīng)中斷
            void lockInterruptibly() throws InterruptedException;

            // 非阻塞式響應(yīng)中斷能立即返回,獲取鎖返回true反之返回false
            boolean tryLock();

            // 超時(shí)獲取鎖,在超時(shí)內(nèi)或者未中斷的情況下能獲取鎖
            boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

            void unlock();

            // 獲取與lock綁定的等待通知組件,當(dāng)前線程必須獲得了鎖才能進(jìn)行等待,進(jìn)行等待時(shí)會(huì)先釋放鎖,當(dāng)再次獲取鎖時(shí)才能從等待中返回
            Condition newCondition();
        }


        進(jìn)行加鎖主要有l(wèi)ock()、tryLock()、lockInterruptibly()這三個(gè)方法。tryLock()方法只是嘗試進(jìn)行獲取當(dāng)前鎖,獲取到了則返回true,獲取不到返回false,不會(huì)直接讓線程加入到等待隊(duì)列中進(jìn)行阻塞等待。lock()方法則會(huì)直接讓線程阻塞等待,使用unlock才能喚醒線程。lockInterruptibly()和lock()方法主要的區(qū)別在于對(duì)異常的處理。


        二、公平鎖介紹

        從源碼中我們可以看出ReentrantLock使用了適配器設(shè)計(jì)模式,ReentrantLock繼承了lock方法,同時(shí)擁有Sync類,Lock的方法都是大部分調(diào)用Sync類實(shí)現(xiàn)的,Sync類繼承了AbstractQueuedSynchronizer類,該類是隊(duì)列同步器會(huì)將等待的線程加入到隊(duì)列的末端等待喚醒。通過(guò)公平鎖的例子,講解一下lock幾個(gè)主要的方法。

         // ReentrantLock的lock方法調(diào)用了sync的lock方法
            public void lock() {
                sync.lock();
            }


        // 公平鎖類
          static final class FairSync extends Sync {
                private static final long serialVersionUID = -3000897897090466540L;

          // ReentrantLock公平鎖模式下lock調(diào)用的方法
                final void lock() {
                    acquire(1);
                }

                /**
                 * Fair version of tryAcquire.  Don't grant access unless
                 * recursive call or no waiters or is first.
                 */
                protected final boolean tryAcquire(int acquires) {
                 // 獲取當(dāng)前調(diào)用獲取鎖方法的線程
                    final Thread current = Thread.currentThread();
                    // 獲取當(dāng)前鎖的狀態(tài),如果c=0表示還沒(méi)有被解鎖,可以進(jìn)行競(jìng)爭(zhēng)獲取鎖
                    int c = getState();
                    // 表明重入鎖沒(méi)有被搶占
                    if (c == 0) {
                        // 等待對(duì)象中沒(méi)有其他需要處理的 就cas將鎖的狀態(tài)設(shè)置為acquires。公平鎖與非公平鎖主要的區(qū)別在于是否是要要讓隊(duì)列等待的先進(jìn)行執(zhí)行。
                        if (!hasQueuedPredecessors() &&
                            compareAndSetState(0, acquires)) {
                            // 把獨(dú)占線程設(shè)置為自己
                            setExclusiveOwnerThread(current);
                            return true;
                        }
                    }
                    // 走到這里表明鎖被鎖住了
                    // 如果鎖的獨(dú)占線程是自己,則直接重入
                    else if (current == getExclusiveOwnerThread()) {
                     // 記錄鎖被重入的次數(shù),在釋放鎖的時(shí)候判斷是否滿足釋放條件
                        int nextc = c + acquires;
                        // 出現(xiàn)異常
                        if (nextc < 0)
                            throw new Error("Maximum lock count exceeded");
                        // 因?yàn)楫?dāng)前鎖被獨(dú)占了所有其他線程是無(wú)法獲取這個(gè)鎖的,state的狀態(tài)只能被自己修改,所以不需要處理并發(fā)的情況。
                        setState(nextc);
                        // 獲取鎖成功
                        return true;
                    }
                    // 獲取鎖失敗
                    return false;
                }
            }

             /**
             * Acquires in exclusive mode, ignoring interrupts.  Implemented
             * by invoking at least once {@link #tryAcquire},
             * returning on success.  Otherwise the thread is queued, possibly
             * repeatedly blocking and unblocking, invoking {@link
             * #tryAcquire} until success.  This method can be used
             * to implement method {@link Lock#lock}.
             *
             * @param arg the acquire argument.  This value is conveyed to
             *        {@link #tryAcquire} but is otherwise uninterpreted and
             *        can represent anything you like.
             */
            // 同步狀態(tài)不允許獲取
            // 如果不在隊(duì)列,當(dāng)前線程放入隊(duì)列
            // 可能會(huì)阻塞當(dāng)前前程
            // 如果當(dāng)前線程在隊(duì)列,將其移出隊(duì)列
            public final void acquire(int arg) {
             // 先嘗試tryAcquire方法獲取鎖,如果失敗了不進(jìn)行阻塞直接返回,調(diào)用acquireQueued將當(dāng)前線程封裝成一個(gè)節(jié)點(diǎn)并進(jìn)行阻塞,加入到等待隊(duì)列中。
             // Node.EXCLUSIVE
                if (!tryAcquire(arg) &&
                    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                    selfInterrupt();
            }


             /**
             * Creates and enqueues node for current thread and given mode.
             *
             * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
             * @return the new node
             */
            private Node addWaiter(Node mode) {
             // 將當(dāng)前的線程封裝成一個(gè)Node,在ReentrantLock鎖是獨(dú)占模式,所以是Node.EXCLUSIVE。
                Node node = new Node(Thread.currentThread(), mode);
                // Try the fast path of enq; backup to full enq on failure
                // 獲取等待隊(duì)列中的最后一個(gè)節(jié)點(diǎn)
                Node pred = tail;
                if (pred != null) {
                 // 走到這里表明tail節(jié)點(diǎn)存在
                 // 將當(dāng)前node的prev節(jié)點(diǎn)設(shè)置為tail節(jié)點(diǎn)
                    node.prev = pred;
                    // cas將tail節(jié)點(diǎn)設(shè)置為新的節(jié)點(diǎn),如果設(shè)置成功表明tail節(jié)點(diǎn)沒(méi)有被修改過(guò),如果失敗表明其他線程提前修改的tail節(jié)點(diǎn),出現(xiàn)了沖突。
                    if (compareAndSetTail(pred, node)) {
                     // 如果沒(méi)有出現(xiàn)沖突,表明當(dāng)前節(jié)點(diǎn)添加節(jié)點(diǎn)到末尾成功
                        pred.next = node;
                        return node;
                    }
                }
                // tail節(jié)點(diǎn)被其他線程修改時(shí)走到這里
                enq(node);
                return node;
            }


             /**
             * Inserts node into queue, initializing if necessary. See picture above.
             * @param node the node to insert
             * @return node'
        s predecessor
             */
             // 當(dāng)tail節(jié)點(diǎn)不存在時(shí)或者tail節(jié)點(diǎn)cas替換失敗時(shí)調(diào)用這個(gè)方法
            private Node enq(final Node node) {
                for (;;) {
                 // 獲取隊(duì)列的末尾節(jié)點(diǎn)
                    Node t = tail;
                    // 如果tail節(jié)點(diǎn)不存在表明這個(gè)隊(duì)列還沒(méi)有進(jìn)行初始化或者已經(jīng)被清空了
                    if (t == null) { // Must initialize
                     // cas初始化了一個(gè)隊(duì)列,防止多個(gè)線程進(jìn)行初始化出現(xiàn)沖突
                        if (compareAndSetHead(new Node()))
                         // 初始化隊(duì)列
                            tail = head;
                    } else {
                     // 如果隊(duì)列存在則不斷調(diào)用這個(gè)方法進(jìn)行cas替換,知道成功插入到隊(duì)列末尾。
                        node.prev = t;
                        if (compareAndSetTail(t, node)) {
                            t.next = node;
                            return t;
                        }
                    }
                }
            }


            /**
             * Acquires in exclusive uninterruptible mode for thread already in
             * queue. Used by condition wait methods as well as acquire.
             *
             * @param node the node
             * @param arg the acquire argument
             * @return {@code trueif interrupted while waiting
             */
             // cas搶占鎖的時(shí)候失敗,將線程插入到等待隊(duì)列中
            final boolean acquireQueued(final Node node, int arg) {
                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)就是head,表明當(dāng)前線程可以嘗試競(jìng)爭(zhēng)鎖
                        if (p == head && tryAcquire(arg)) {
                         // 走到這里表明當(dāng)前node競(jìng)爭(zhēng)到了鎖
                         // 將當(dāng)前節(jié)點(diǎn)設(shè)置為head
                            setHead(node);
                            p.next = null; // help GC
                            failed = false;
                            return interrupted;
                        }
                        // 如果不是head,或者競(jìng)爭(zhēng)鎖失敗了,調(diào)用shouldParkAfterFailedAcquire讓前一個(gè)節(jié)點(diǎn)完事兒了把自己?jiǎn)拘?,同時(shí)進(jìn)入等待
                        if (shouldParkAfterFailedAcquire(p, node) &&
                            parkAndCheckInterrupt())
                            interrupted = true;
                    }
                } finally {
                    if (failed)
                        cancelAcquire(node);
                }
            }


            /**
             * Checks and updates status for a node that failed to acquire.
             * Returns true if thread should block. This is the main signal
             * control in all acquire loops.  Requires that pred == node.prev.
             *
             * @param pred node's predecessor holding status
             * @param node the node
             * @return {@code true} if thread should block
             */
            private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
             // 獲取前一個(gè)node的waitStatus
                int ws = pred.waitStatus;
                // 如果已經(jīng)是SIGNAL表明前一個(gè)節(jié)點(diǎn)一直知道要通知自己了,可以放心進(jìn)行睡眠等待了
                if (ws == Node.SIGNAL)
                    /*
                     * This node has already set status asking a release
                     * to signal it, so it can safely park.
                     */
                    return true;
                // 走到這里表明前面一個(gè)節(jié)點(diǎn)不為SIGNAL
                // 下面的方法是尋找一個(gè)非cancel節(jié)點(diǎn),把它設(shè)置為signal用來(lái)喚醒自己
                if (ws > 0) {
                    /*
                     * Predecessor was cancelled. Skip over predecessors and
                     * indicate retry.
                     */
                    do {
                        node.prev = pred = pred.prev;
                    } while (pred.waitStatus > 0);
                    pred.next = node;
                } else {
                    /*
                     * waitStatus must be 0 or PROPAGATE.  Indicate that we
                     * need a signal, but don'
        t park yet.  Caller will need to
                     * retry to make sure it cannot acquire before parking.
                     */
                     // 把前一個(gè)節(jié)點(diǎn)設(shè)置為signal用來(lái)喚醒自己
                    compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
                }
                return false;
            }


            /**
             * Convenience method to park and then check if interrupted
             *
             * @return {@code trueif interrupted
             */
             // 阻塞當(dāng)前線程
            private final boolean parkAndCheckInterrupt() {
             // 調(diào)用LockSupprt方法將當(dāng)前線程進(jìn)行阻塞
                LockSupport.park(this);
                return Thread.interrupted();
            }


            /**
             * Releases in exclusive mode.  Implemented by unblocking one or
             * more threads if {@link #tryRelease} returns true.
             * This method can be used to implement method {@link Lock#unlock}.
             *
             * @param arg the release argument.  This value is conveyed to
             *        {@link #tryRelease} but is otherwise uninterpreted and
             *        can represent anything you like.
             * @return the value returned from {@link #tryRelease}
             */
            // 更新同步狀態(tài)
            // 同步狀態(tài)是否允許被阻塞的線程獲取
            public final boolean release(int arg) {
                // 1. 釋放鎖
                if (tryRelease(arg)) {
                    // 2. 如果獨(dú)占鎖釋放"完全",喚醒后繼節(jié)點(diǎn)
                    Node h = head;
                    if (h != null && h.waitStatus != 0)
                        unparkSuccessor(h);
                    return true;
                }
                return false;
            }

                protected final boolean tryRelease(int releases) {
                    int c = getState() - releases;
                    if (Thread.currentThread() != getExclusiveOwnerThread())
                        throw new IllegalMonitorStateException();
                    boolean free = false;
                    // 如果重入次數(shù)為零表示可以進(jìn)行釋放
                    if (c == 0) {
                        free = true;
                        setExclusiveOwnerThread(null);
                    }
                    setState(c);
                    return free;
                }

            /**
             * 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)
                         // 獲取最后一個(gè)沒(méi)有被cancel的節(jié)點(diǎn)
                            s = t;
                }
                if (s != null)
                 // 調(diào)用LockSupport.unpark釋放下一個(gè)被阻塞且沒(méi)有被取消的線程來(lái)競(jìng)爭(zhēng)鎖
                    LockSupport.unpark(s.thread);
            }


        簡(jiǎn)單使用

        public class Main {
            public static void main(String[] args) {
                ReentrantLock lock = new ReentrantLock();
                Thread thread1 = new Thread(()->{
                    System.out.println("thread1 wait lock");
                    lock.lock();
                    System.out.println("thread1 get lock");
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.unlock();
                    System.out.println("thread1 unlock");
                });
                Thread thread2 = new Thread(()->{
                    System.out.println("thread2 wait lock");
                    lock.lock();
                    System.out.println("thread2 get lock");
                    try {
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    lock.unlock();
                    System.out.println("thread2 unlock");
                });
                thread1.start();
                thread2.start();
            }
        }


        ————————————————

        版權(quán)聲明:本文為CSDN博主「zhanghl111」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。

        原文鏈接:

        https://blog.csdn.net/zhlily1/article/details/115794503






        粉絲福利:Java從入門(mén)到入土學(xué)習(xí)路線圖

        ??????

        ??長(zhǎng)按上方微信二維碼 2 秒


        感謝點(diǎn)贊支持下哈 

        瀏覽 43
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美xxxxxx视频 | 免费精品﹣色哟哟 | 欧美三级性爱视频 | 91国产精品在线 | 亚洲男人天堂2023 | 欧美成人黄色 | www日本免费 | 韩国日本三级片视频 | 黄色操逼视频免费观看 | 嗯~啊~快点死我漫画 |