• ReentrantLock源码分析


    ReentrantLock源码分析

    ReentrantLock是独享锁,同时是基于AQS实现的,因此它底层肯定是通过自定义AQS独占模式下的同步器来实现独享锁,该同步器需要重写AQS提供的tryAcquire()和tryRelease()方法,只需要告诉AQS是否尝试获取同步资源和释放同步资源成功即可。

    AQS子类需要定义以及维护同步状态的值,在ReentrantLock中使用state为0表示锁没有被线程所持有,使用state不为0表示锁经被线程所持有。

    ReentrantLock有公平和非公平两种模式,公平模式即多线程按照申请锁的顺序来获取锁,非公平模式即多线程并非按照申请锁的顺序来获取锁。


    ReentrantLock的结构

    public class ReentrantLock implements Lock, java.io.Serializable {
        
        private final Sync sync;
    
        /**
         * 抽象同步器(AQS独占模式)
         */
        abstract static class Sync extends AbstractQueuedSynchronizer {
           // ......
        }
        
        /**
         * 非公平同步器
         */
        static final class NonfairSync extends Sync {
           // ......
        }
    
        /**
         * 公平同步器
         */
        static final class FairSync extends Sync {
          // ......
        }
    
        public ReentrantLock() {
            sync = new NonfairSync();
        }
    
        public ReentrantLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
        }
    
        public void lock() {
            sync.lock();
        }
    
        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
    
        public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }
    
        public void unlock() {
            sync.release(1);
        }
      
        // 其它省略
    }
    

    可以看到ReentrantLock中定义了一个抽象同步器(Sync)、非公平同步器(NonfairSync)、公平同步器(FairSync),同时非公平同步器和公平同步器都继承抽象同步器。

    同时ReentrantLock中存在一个全局的抽象同步器属性,然后通过构建方法来进行初始化,通过fair参数来指定到底是使用公平同步器还是非公平同步器,默认情况下是使用非公平同步器。

    同时ReentrantLock中的lock()方法将会调用抽象同步器声明的lock()方法,unlock()方法将会直接调用AQS的release()方法,而tryLock()方法将会调用抽象同步器提供的nonfairTryAcquire()方法。


    剖析抽象同步器

    /**
     * 抽象同步器(AQS独占模式)
     */ 
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
    
        /**
         * 声明了lock()方法,用于加锁
         * ReentrantLock的lock()方法会调用抽象同步器声明的lock()方法
         */ 
        abstract void lock();
    
        /**
         * 尝试获取锁,如果获取成功则返回true,否则返回false
         * ReentrantLock的tryLock()方法会调用抽象同步器提供的nonfairTryAcquire()方法
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果同步状态为0,表示锁没有被线程所持有
            if (c == 0) {
                // 则通过CAS将同步状态设置为1,表示获取锁
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current); // 将拥有同步资源的线程设置为当前线程
                    return true;
                }
            }
            // 锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还尝试获取锁,那么就累加同步状态的值,这种情况也返回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;
        }
    
        /**
         * 重写AQS的tryRelease()方法,尝试释放锁,如果释放成功,则返回true,否则返回false
         */ 
        protected final boolean tryRelease(int releases) {
      		// 获取锁释放后剩余的同步资源
            int c = getState() - releases;
            // 如果当前线程并非拥有锁的线程,则直接抛出异常(要求谁加的锁只能由谁进行释放)
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            
            boolean free = false;
            // 如果锁释放后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null); // 清空拥有同步资源的线程
            }
            setState(c);
            return free;
        }
    
        /**
         * 判断当前线程是否持有锁
         */
        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
    
        /**
         * 返回Condition实例,在某些场景下可以进行阻塞与唤醒
         */
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    
    	/**
    	 * 获取拥有锁的线程
    	 */
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
    
        /**
         * 返回当前线程拥有锁的个数
         */
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
    
        /**
         * 判断锁是否已经被线程所持有
         */
        final boolean isLocked() {
            return getState() != 0;
        }
    
    }
    

    抽象同步器声明了lock()方法,该方法需要由抽象同步器的子类来进行实现,ReentrantLock的lock()方法将会调用抽象同步器声明的lock()方法。

    同时抽象同步器提供了nonfairTryAcquire()方法,如果同步状态的值为0,表示锁没有被线程所持有,则通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有了,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么就累加同步状态的值,这种情况也会返回true,否则返回false, 该方法会在ReentrantLock的tryLock()方法以及非公平同步器的tryAcquire()方法中被调用。

    同时抽象同步器已经重写了AQS的tryRelease()方法,因此抽象同步器的子类还需要重写AQS的tryAcquire()方法。


    剖析非公平同步器

    /**
     * 非公平同步器(继承抽象同步器)
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
    
        /**
         * 实现抽象同步器声明的lock()方法
         */
        final void lock() {
            // 直接通过CAS尝试获取锁,如果失败则调用acquire()方法
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    
        /**
         * 重写AQS的tryAcquire()方法,直接调用抽象同步器的nonfairTryAcquire()方法
         */
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    

    非公平模式下的ReentrantLock的加锁,将会调用非公平同步器的lock()方法,该方法先通过CAS尝试获取锁,如果获取失败则调用AQS的acquire()方法,该方法又会调用非公平同步器的tryAcquire()方法。

    非公平模式下的ReentrantLock的解锁,将会调用AQS的release()方法,该方法又会调用抽象同步器提供的tryRelease()方法。


    剖析公平同步器

    /**
     * 公平的同步器(继承抽象同步器)
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
    
        /**
         * 实现抽象同步器声明的lock()方法
         */
        final void lock() {
            // 直接调用acquire()方法
            acquire(1);
        }
        
    	/**
         * 重写AQS的tryAcquire()方法
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 如果同步状态为0,同时当前线程就是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或者只有一个节点,那么才会通过CAS尝试获取锁(这个判断用于保证是公平锁)
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current); // 将拥有同步资源的线程设置为当前线程
                    return true;
                }
            }
            // 如果同步状态不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还尝试获取锁,那么就累加同步状态的值,这种情况也返回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;
        }
    }
    

    公平模式下的ReentrantLock的加锁,将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法,该方法又会调用公平同步器的tryAcquire()方法。

    公平模式下的ReentrantLock的解锁,将会调用AQS的release()方法,该方法又会调用抽象同步器提供的tryRelease()方法。


    非公平模式下的总结

    1.当线程要获取锁时,可以直接调用ReentrantLock的lock()和tryLock()方法。

    2.如果调用lock()方法,那么将会调用非公平同步器的lock()方法,该方法会通过CAS尝试获取锁,如果获取锁成功则直接返回,做自己的事情,否则将会调用AQS的acquire()方法。

    3.AQS的acquire()方法又会调用非公平同步器的tryAcquire()方法,该方法直接调用抽象同步器的nonfairTryAcquire()方法,如果同步状态的值为0,表示锁没有被线程所持有,则通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有,如果当前线程就是拥有锁的线程,它还进行加锁,那么就累加同步状态的值,这种情况也会返回true,否则返回false。

    4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法,通过CAS尝试获取锁。

    5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法将会直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。


    公平模式下的总结

    1.当线程要获取锁时,可以直接调用ReentrantLock的lock()和tryLock()方法。

    2.如果调用lock()方法,那么将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法。

    3.AQS的acquire()方法又会调用公平同步器的tryAcquire()方法,同时该方法中只有当同步状态的值为0,同时当前线程是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或只有一个节点时,才会通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么累加同步状态的值,这种情况也会返回true,否则返回false。

    4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法,通过CAS尝试获取锁。

    5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法将会直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。


    FAQ

    关于ReentrantLock的tryLock()方法

    ReentrantLock的tryLock()方法是非公平的,因为无论在什么模式下,该方法都会调用抽象同步器的nonfairTryAcquire()方法,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程直接调用了tryLock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。

    关于获取了锁的线程能否再进行加锁?

    是可以的,因为无论在非公平还是公平模式下,tryAcquire()方法中都会有这么一个判断,如果同步状态的值不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么累加同步状态的值,这种情况方法也会返回true,同时在抽象同步器的tryRelease()方法中,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。

    非公平锁是如何实现非公平的?

    主要体现在非公平同步器的lock()方法,当线程要进行加锁时,并没有直接调用AQS的acquire()方法(虽然acquire也会调用tryAcquire),而是先通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程直接调用了lock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。

    公平锁是如何实现公平的?

    主要体现在公平同步器的lock()和tryAcquire()方法,首先lock()方法直接调用AQS的acquire()方法,并没有像非公平同步器的lock()方法一样,先通过CAS尝试获取锁,然后在tryAcquire()方法中,只有当同步状态的值为0,同时当前线程就是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或只有一个节点时,才会通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程调用了lock()方法进行加锁,由于lock()方法直接调用AQS的acquire()方法,然后acquire()方法又调用公平同步器的tryAcquire()方法,虽然此时同步状态的值为0,但是当前线程并不是等待队列中头节点的后继节点所封装的线程,因此该线程也只能封装成Node节点,然后加入到等待队列当中。

  • 相关阅读:
    selenium2截图ScreenShot的使用
    selenium2断言类Assert的使用
    selenium2中的TestNg注解和数据驱动的简介及使用
    bash函数定义/使用/传参…
    bash字符串操作
    bash数组操作-定义/初始化/赋值…
    bash实例-参数/函数/统计IP
    01.AutoMapper 之约定(Conventions)
    00.AutoMapper 之入门指南(Getting Started Guide)
    AutoMapper
  • 原文地址:https://www.cnblogs.com/funyoung/p/13623109.html
Copyright © 2020-2023  润新知