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

来源:互联网 发布:真正刷钻软件 编辑:程序博客网 时间:2024/06/06 02:53

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
原创粉丝点击