ReentrantLock的公平锁解析

如果你不相信努力和时光,那么成果就会是第一个选择辜负你的。不要去否定你自己的过去,也不要用你的过去牵扯你现在的努力和对未来的展望。不是因为拥有希望你才去努力,而是去努力了,你才有可能看到希望的光芒。ReentrantLock的公平锁解析,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

前置概念

park和unpark

unpark函数为线程提供“许可(permit)”,线程调用park函数则等待“许可”。
		Thread thread = Thread.currentThread();
        LockSupport.park(); //park()是不可重入的,如果连续调用两次park(),会发生线程阻塞
        LockSupport.unpark(thread); //和wait和notify相比,unpark()可以放在park之前

这个有点像信号量,但是这个“许可”是不能叠加的,“许可”是一次性的。

  • 比如线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态。注意,unpark函数可以先于park调用。比如线程B调用unpark函数,给线程A发了一个“许可”,那么当线程A调用park时,它发现已经有“许可”了,那么它会马上再继续运行。
  • 详细参考:lock详解

多线程执行逻辑

一般分两种情况,一种是阻塞抢占式 ,一种是线程交替执行(顺序可以控制)

这里对AQS类做一个介绍

  1. 里面有一个静态内部类Node,我们主要关注四个属性:prev,next,thread,waitStatus
		volatile Node prev;
        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  The enq operation does not
         * assign next field of a predecessor until after attachment,
         * so seeing a null next field does not necessarily mean that
         * node is at end of queue. However, if a next field appears
         * to be null, we can scan prev's from the tail to
         * double-check.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */
        volatile Node next;

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        volatile Thread thread;
         /**
         * The synchronization state.
         */
        private volatile int state;
  1. AbstractQueuedSynchronizer类里面还有三个关键属性
	 /**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

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

公平锁源码分析

以如下源码作为例子:

	    ReentrantLock reentrantLock = new ReentrantLock(true);
        reentrantLock.lock();

深入分析

首先调用ReentrantLock 的lock()方法:

public void lock() {
       sync.lock();
    }

然后按ctrl+鼠标左键,点击sync.lock()方法,进入如下方法:

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

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();
        ...

因为我们讨论的是非公平锁,所以点击lock()方法,按ctrl+alt+b,点击fairSysc进入:

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

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

继续往下看,就来到了AbstractQueuedSynchronizer类,就是通常说的AQS:

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

调用了tryAcquire()方法,acquireQueued()方法,selfInterrupt()方法
在这里插入图片描述
下面我们关注tryAcquire()方法在公平锁里面的实现:

		/**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

下一步主要看hasQueuedPredecessors()方法:

 public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        //null != null -> false,到了!hasQueuedPredecessors()方法就变成了true
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
   }

公平锁的实现

主要是利用AQS维护了一个链表,一个头结点和一个尾节点,利用这两个节点来设置线程的执行操作

在这里插入图片描述

细节点

head节点

只要维护队列,就会产生一个哨兵节点,head指针指向哨兵节点,
每次更新的时候是把哨兵节点进行替换,不是直接修改指针

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/202539.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!