源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁

ReentrantLock

这篇文章是从JDK8的ReentrantLock源码角度来分析ReentrantLock是怎么利用AQS来实现公平锁,和非公平锁的。

所以前提需要理解AQS。

简单的翻译了一下,大概清楚JDK doc里ReentrantLock介绍

   /**
     * A reentrant mutual exclusion {@link Lock} with the same basic
     * behavior and semantics as the implicit monitor lock accessed using
     * {@code synchronized} methods and statements, but with extended
     * capabilities.

      ReentrantLock属于非共享锁,即独占锁,每次只能是一个线程取得锁资源,它与synchronized关键字具有相同的锁语义,但是ReentrantLock具有一些另外的拓展功能

     *
     * A {@code ReentrantLock} is owned by the thread last
     * successfully locking, but not yet unlocking it. A thread invoking
     * {@code lock} will return, successfully acquiring the lock, when
     * the lock is not owned by another thread. The method will return
     * immediately if the current thread already owns the lock. This can
     * be checked using methods {@link #isHeldByCurrentThread}, and {@link
     * #getHoldCount}.

      ReentrantLock锁只属于获得它的线程,直到unlock解锁。如果一个线程执行lock(),如果当前锁没有
      其他线程在占用,则会获取得锁,立即返回。
      如果该线程已经拥有当前锁,再执行lock(),也会再次获得锁,立即返回。
      当前线程是否持有当前锁,可以用锁的isHeldByCurrentThread()来查看获得当前锁的线程。
     *
     * <p>The constructor for this class accepts an optional
     * <em>fairness</em> parameter.  When set {@code true}, under
     * contention, locks favor granting access to the longest-waiting
     * thread.  Otherwise this lock does not guarantee any particular
     * access order.  
     ReentrantLock的构造函数可以传入一个boolean类型的true,来表示构造一个公平的锁,此时锁倾向于最长
     等待时间的线程。
     如果默认。或者传入函数为false。则为非公平锁,此时不保证获取得到锁的线程顺序。
     Programs using fair locks accessed by many threads
     * may display lower overall throughput (i.e., are slower; often much
     * slower) than those using the default setting, but have smaller
     * variances in times to obtain locks and guarantee lack of
     * starvation. 
     使用公平锁的吞吐率其实在某些情况会比使用非公平锁低,但是可以解决饥饿现象
     Note however, that fairness of locks does not guarantee
     * fairness of thread scheduling. Thus, one of many threads using a
     * fair lock may obtain it multiple times in succession while other
     * active threads are not progressing and not currently holding the
     * lock.
     * Also note that the untimed {@link #tryLock()} method does not
     * honor the fairness setting. It will succeed if the lock
     * is available even if other threads are waiting.

      值得注意的是,tryLock()属于非阻塞方法,成功则返回true,失败则返回false,
      这个方法无所谓的公平和非公平之分,即使是设置的是公平锁,任何线程调用这个方法,都会尝试获得锁资源,
      即使等待队列中有其他线程正在等待该锁资源
     *
     * <p>It is recommended practice to <em>always</em> immediately
     * follow a call to {@code lock} with a {@code try} block, most
     * typically in a before/after construction such as:
     *
     *  <pre> {@code
     * class X {
     *   private final ReentrantLock lock = new ReentrantLock();
     *   // ...
     *
     *   public void m() {
     *     lock.lock();  // block until condition holds
     *     try {
     *       // ... method body
     *     } finally {
     *       lock.unlock()
     *     }
     *   }
     * }}</pre>
     *
     * <p>In addition to implementing the {@link Lock} interface, this
     * class defines a number of {@code public} and {@code protected}
     * methods for inspecting the state of the lock.  Some of these
     * methods are only useful for instrumentation and monitoring.
     *
     * <p>Serialization of this class behaves in the same way as built-in
     * locks: a deserialized lock is in the unlocked state, regardless of
     * its state when serialized.
     *
     * <p>This lock supports a maximum of 2147483647 recursive locks by
     * the same thread. Attempts to exceed this limit result in
     * {@link Error} throws from locking methods.

        同一线程最大可重入锁的次数是 2的31次方减1,就是2147483647
     *
     * @since 1.5
     * @author Doug Lea
     */

IDEA里面的UML图显示,ReentranLock里面组合了Sync内部类,而Sync是继承了AQS。

其次,NonfairSync,fairSync是集成自Sync的两个内部类,分别对应非公平与公平的AQS。

这里写图片描述

再仔细看,NonfairSync,fairSync只是重写了Sync的lock()方法和重写了AQS的tryAcquire()

这里写图片描述

首先是,构造函数

  public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

再来看看内部继承AQS的同步器Sync,Sync作为ReentrantLock的内部类。

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();       //  NonfairSync,fairSync 要继承此Sync要实现的

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.

           注意这里,因为Lock接口的语义里,tryLock()是不分公平与非公平的,(实际语义其实等同于非公平的)
           所以初始构造ReentrantLock时候,无论是new的fairSync还是notfairSync,都应该可以执行tryLock(),

           因此tryLock()用到的Sync(AQS)的nonfairTryAcquire(),应该在NonfairSync,fairSync父类中实现,就是此类Sync,这样才可以达到共用的效果。

         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {      //CAS安全地更新state
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {        //检查是否是重入
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);        //此时是重入,所以这里可以不用CAS来改变state
                return true;
            }
            return false;
        }

       //此tryRelease为NonfairSync,fairSync共用,因为释放无所谓公平不公平

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {           //state==0  才解除线程持有锁
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

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

        // Methods relayed from outer class

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

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

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

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

再来看看 NonfairSync ,也就是默认的非公平模式

 /**
     * 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.
         */
        @Override    //重写父类Sync的抽象方法Lock(),其实公平和非公平,区别在这,非公平lock()的时候,都会先尝试获取锁资源,失败了,再进入acquire()流程。


      //而公平锁却直接进入acquire()流程,需要检验等待队列中是否有其他线程在等待,若无,才尝试获取锁

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


      //直接调用的是Sync定义的nonfairTryAcquire()
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

再来看FairSync

   /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;


      //这里不同于非公平,这里直接进入acquire()
        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) {
            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;
        }
    }

所以说,非公平锁,lock的时候,会无视正在等待锁资源的队列里面是否有成员,而直接尝试一次获取,若不成功,则还是会进入AQS的CLH等待队列,然后阻塞,顺序等待唤醒,获取。

公平锁,lock的时候,则不能无视正在等待锁资源的队列里面的成员。

最后,看下ReentrantLock的提供给我们使用的接口函数,是怎样调用fairSync,nonfairSync的

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

分派给NonfairSync的lock()

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

分派给fairSync的lock()

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

而tryLock(),直接是用的sync内部类定义的nonfairTryAcquire(),这也是为什么nonfairTryAcquire()定义在Sync,而不是NonfairSync()的原因。

  public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

总结,Doug Lea,非常巧妙的利用AQS的模板方法,简洁而清晰地实现了具有公平和非公平的模式的ReentrantLock,值得我们去学习。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值