美文网首页
11-ReentrantLock

11-ReentrantLock

作者: 鹏程1995 | 来源:发表于2020-02-05 09:33 被阅读0次

    类介绍

    类继承关系

    1.png

    ReentrantLock实现了Lock接口和Serializable接口。

    类介绍

    可重入互斥锁,此锁依赖AQS实现了最基本的互斥锁,提供了线程在占有锁的情况下可重复获得锁的功能。

    此对象的实现思路很简单,提供了一个对象继承AQS,对线程的操作全部转化为直接或者间接对AQS的方法的依赖。

    注意

    ReentrantLock提供了两种获得锁的方法,一种是不公平的,一种是相对公平的。

    公平的那个不保证一定公平。这个我们在具体介绍时会详细介绍。

    遗留问题

    获得/释放锁时的入参 argAQS的状态state,在这里赋予了它特别的意义:占有锁的线程重入次数

    源码

    内部类

    Sync

    类介绍

    此类是ReentrantLock使用AQS的基础类,此类对AQSReentrantLock需要的、公平模式和非公平模式下都要用的方法进行了实现。

    方法介绍

    /**
     * 可重入互斥锁的同步器的基础类。定义了两个他的子类,一个是公平的、一个是非公平的
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // 自定义 serialVersionUID ,方便代码变化后做序列化、反序列化兼容
        private static final long serialVersionUID = -5179523762034025860L;
    
        /**
         * 留给子类实现的接口,根据公平/不公平实现不同
         */
        abstract void lock();
    
        /**
         * 不公平模式下的 tryLock() 实现,由于在 ReentrantLock 中也直接调用了,
         * 所以算是通用的方法吧,放这里
         *
         * 入参为获得锁的入参 arg
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            // 获得当前锁被它的占有线程重入的次数
            int c = getState();
            // 当前锁没人用,可竞争
            if (c == 0) {
                // 用CAS来竞争,竞争到就占用成功
                // 没竞争到就算了
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 当前锁被占用,看看是不是被自己占用的
            else if (current == getExclusiveOwnerThread()) {
                // 自己占用的,重入!
                int nextc = c + acquires;
                // 如果溢出了就报错,重入次数过多
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                
                // 这里和上面不同,这里只有自己在用,直接设置即可,上面使用 CAS 是
                // 因为锁被释放,有多个线程在竞争,可能有多个线程在同时修改 state 那
                // 个变量
                setState(nextc);
                return true;
            }
            return false;
        }
    
        // 互斥模式的释放都一样,不涉及公不公平的问题,所以就在这里实现了
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            
            // 本线程不占有锁,不能继续执行
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            
            // 释放完锁被释放了
            if (c == 0) {
                free = true;
                // 清除对锁的占用
                setExclusiveOwnerThread(null);
            }
            // 这里将 c 设置成0,才有人竞争,所以这里还是一个线程操作,不用 CAS 
            setState(c);
            return free;
        }
    
        protected final boolean isHeldExclusively() {
            // 判断当前线程是否占用锁
            return getExclusiveOwnerThread() == Thread.currentThread();
        }
    
        final ConditionObject newCondition() {
            return new ConditionObject();
        }
    
        // 为外部类提供的方法
    
        // 不明白为什么要这么判断一下,没必要,即使锁没有被占用返回的也是 null
        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }
    
        // 当前的锁都是互斥锁,可能是怕被错误调用吧
        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }
    
        
        final boolean isLocked() {
            return getState() != 0;
        }
    
        /**
         * 从输入流中反序列化
         *
         * 反序列化后将锁重置为未获取状态
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }
    
    

    NonfairSync

    类介绍

    Sync的子类,提供不公平的等待

    方法介绍

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
    
        /**
         * 非公平锁,有线程来了,先竞争一下锁,如果竞争到了,更改了锁的状态,就直接运行
         *
         * 失败了就走 AQS 的正经流程:
         *    先尝试调用 tryAcquire 获得一下锁,失败就进队列阻塞,等待获得锁后被唤醒
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    

    思路提要

    注意,正常的 AQS子类中提供了两个插队的地方:

    1. lock()可以插队,有线程来了先竞争一下锁
    2. AQS.acquire()可以插队,一般都是先调用tryAcqurie()竞争一下,失败了再乖乖进队

    FairSync

    类介绍

    Sync的子类,提供公平的等待。

    方法介绍

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
    
        final void lock() {
            acquire(1);
        }
    
        /**
         * 做判断,如果同步队列中有前驱就直接获得失败
         */
        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;
        }
    }
    
    

    内部方法

    提供了一些方便的用于查询锁当前状态的方法,可以用来做监视器等。

    这里直接把方法粘过来了,都很简单

    // 获得重入次数
    public int getHoldCount() {
        return sync.getHoldCount();
    }
    
    // 判断是否被当前线程获得
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }
    
    // 判断锁是否被占用
    public boolean isLocked() {
        return sync.isLocked();
    }
    
    // 判断当前同步器是否公平
    public final boolean isFair() {
        return sync instanceof FairSync;
    }
    
    // 获得占用当前锁的线程
    protected Thread getOwner() {
        return sync.getOwner();
    }
    
    // 获得是否有竞争当前锁的线程
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
    
    // 获得入参线程是否在竞争当前锁
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }
    
    // 获得竞争锁的队列长度
    public final int getQueueLength() {
        return sync.getQueueLength();
    }
    
    // 获得竞争锁的所有线程
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }
    
    // 判断是否有线程被 Condition 阻塞等待唤醒
    // 判断条件同步队列中是否有线程
    public boolean hasWaiters(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
    
    // 获得条件同步队列中线程个数
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
    
    // 获得条件同步队列中的线程
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }
    
    

    暴露的方法

    构造方法

    /**
     * 非公平锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    /**
     * 可以公平锁
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    
    

    lock()

    介绍

    阻塞等待获得锁,依赖构造函数生成的Sync

    源码

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

    lockInterruptibly()

    介绍

    阻塞等待锁,可打断,打断抛异常

    源码

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    

    tryLock()

    介绍

    尝试获得锁,非阻塞,根据返回值判断是否获得锁。

    直接竞争锁,不排队,在公平模式下也可以调用

    源码

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

    tryLock(long timeout, TimeUnit unit)

    介绍

    尝试获得锁,限时、可打断,阻塞获得

    返回值表示是否在超时前获得

    源码

    // 返回 true 表示获得锁
    // 返回 false 表示超时
    public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }
    

    unlock()

    介绍

    释放本线程获得的锁【释放一次,也就是重入次数-1,减到0时释放锁】

    源码

    public void unlock() {
        sync.release(1);
    }
    

    newCondition()

    介绍

    创建 Condition对象,用来使获取锁执行一半的线程放弃锁阻塞

    源码

    public Condition newCondition() {
        return sync.newCondition();
    }
    

    扩展

    参考文献

    相关文章

      网友评论

          本文标题:11-ReentrantLock

          本文链接:https://www.haomeiwen.com/subject/vzmjxhtx.html