美文网首页
J.U.C锁之 ReentrantLock

J.U.C锁之 ReentrantLock

作者: 贪睡的企鹅 | 来源:发表于2019-07-05 23:26 被阅读0次

1 ReentrantLock简介

ReentrantLock 用来表示可重入的互斥锁

主要特性

  • 互斥锁:只存在一个竞争资源,同一个时间内只能又一个线程获取,当多个线程同时获取锁时,只有一个线程能获取,其他线程排队等待。当获取释放锁时,只能由一个线程进入。

  • 可重入:获取锁的线程可以在次获取锁。需要注意是如果一个线程多次获取锁,但只释放一次锁。那么此锁还被当前线程占用。

 @Test
    public void testReenter(){
        ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.lock();
        reentrantLock.lock();
        reentrantLock.unlock();
        //如果不执行当前代码,锁依旧没有被当前线程释放
        reentrantLock.unlock();
    }
  • 公平性:支持公平性和非公平性。所谓公平表示在获取锁时逻辑是否要考虑当前正在排队等待线程。按照大白话来说就时公平表示不能插入强占资源。

2 实例原理

ReentrantLock 使用AQS实现锁机制来实现读写锁。AQS是AbstractQueuedSynchronizer的缩写,翻译过来就是"同步器",,它实现了Java函数中锁同步(synchronized),锁等待(wait,notify)功能。

AbstractQueuedSynchronizer是一个抽象类,我们可以编写自己类继承AQS重写获取独占式或共享式同步状态模板方法,实现锁锁同步(synchronized),锁等待(wait,notify)功能

2.1 AQS 实现原理

AQS核心是一个同步状态,两个队列。它们实现了Java函数中锁同步(synchronized),锁等待(wait,notify),并在其基础上实现了独占式同步,共享式同步2中方式锁的实现。

无论独占式还时共享式获取同步状态成功则直接返回,失败则进入CLH同步队列并阻塞当前线程。当获取同步状态线程释放同步状态,AQS会选择从CLH队列head头部节点的第一个节点释放阻塞,尝试重写竞争获取同步状态,如果成功则将当前节点出队。如果失败则继续阻塞。

获取同步状态的线程也可以使用condition对象释放同步状态进入等待队列。只有等待其他线程使用condition.signal或condition.signAll()唤醒被从阻塞状态中释放重新竞争获取同步状态成功后从原来指令位置继续运行。

2.1.1 同步状态

AQS实现了锁,必然存在一个竞争资源。AQS存在从一个int类型的成员变量state,我们把它称为同步状态,同步状态通常用做判断线程能否获取锁的依据

2.1.2 同步队列

AQS 实现了锁那么总需要一个队列将无法获取锁的线程保存起来,方便在锁释放时通知队列中线程去重新竞争锁。

实现原理
同步队列又被称为CLH同步队列,CLH队列是通过链式方式实现FIFO双向队列。当线程获取同步状态失败时,AQS则会将当前线程构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态被释放时,会把首节点后第一个节点的线程从阻塞状态下唤醒,唤醒的线程会尝试竞争同步状态,如果能获取同步状态成功,则从同步队列中出队。

image
2.1.3 Condition & 等待队列
  • Java 传统的监视器有如下函数 wait、notify、notifyAll。它们可以实现当一个线程获取锁时,它可以主动放弃锁进入一个条件队列中。只有其他线程通知时才从条件队列中出队,重新获取锁成功后继续执行之前的未完成代码逻辑。

  • AQS内部存在一个内部类实现了Condition接口,其内部维护着一条链式实现单向等待队列。我们可以使用AQS获取内部实现Condition接口对象,调用await(),signal(),signalAll()函数实现Java中wait、notify、notifyAll同样功能。

实现原理

  • 当获取同步状态的线程调用condition.await(),则会阻塞,并进入一个等待队列,释放同步状态.
  • 当其他线程调用了condition.signal()方法,会从等待队列firstWaiter开始选择第一个等待状态不是取消的节点.添加到同步队列尾部.
  • 当其他线程调用了condition.signalAll()方法,会从等待队列firstWaiter开始选择所有等待状态不是取消的节点.添加到同步队列尾部.

这里取消节点表示当前节点的线程不在参与排队获取锁。

image
2.1.4 独占式同步

从概念上来说独占式对应只存在一个资源,且只能被一个线程或者说竞争者占用.

2.1.5 共享式同步

从概念上来说共享式对应存在多个资源的是有多个线程或者竞争者能够获取占用.

2.2 模板方法

我们可以编写自己类继承AQS选择重写独占式或共享式模板方法,从而定义如何获取同步状态和释放同步状态的逻辑。

2.2.1 独占式

tryAcquire:尝试独占式获取同步状态,返回值为true则表示获取成功,否则获取失败。

tryRelease
尝试独占式释放同步状态,返回值为true则表示获取成功,否则获取失败。

2.2.2 共享式

tryAcquireShared:尝试共享式获取同步状态,当返回值为大于等于0的时获得同步状态成功,否则获取失败。

tryReleaseShared:尝试共享式释放同步状态,返回值为true则表示获取成功,否则获取失败。

2.2 如何基于AQS实现ReentrantLock

由于是互斥锁当然我们选择使用独占同步,Sync需要重写 tryAcquire 获取同步状态条件逻辑,tryRelease释放同步条件逻辑。其核心点在于使用同步状态做判断。当同状态为0时,锁被未被占用,同步状态大于0,锁被占用,每次调用tryAcquire同步状态+1,每次调用tryRelease同步状态-1

实现公平锁

实现公平锁需要在获取锁的逻辑中首先判断同步队列存在存在等待的线程。如果存在则返回false.

实现可重入特性

想要让获取锁的线程能否重新获取锁,我们只需要将获取锁的线程记录下来,每次获取锁的时候比对就好。然后将同步状态+1,同时释放时候只有当同步状态为0时,才释放锁。

2.2 类结构

ReentrantLock 使用AQS实现锁机制,内部存在有三个内部类 Sync、NonfairSync 和 FairSync 类。

  • Sync 继承 AbstractQueuedSynchronizer 抽象类。
  • NonfairSync(非公平锁) 继承 Sync 抽象类。
  • FairSync(公平锁) 继承 Sync 抽象类。
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;


    private final Sync sync;

    /**
     * Sync 继承 AbstractQueuedSynchronizer 抽象类
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        ...省略源代码
    }

    /**
     * NonfairSync(非公平锁) 继承 Sync 抽象类。
     */
    static final class NonfairSync extends Sync {
        ...省略源代码
    }

    /**
     * FairSync(公平锁) 继承 Sync 抽象类。
     */
    static final class FairSync extends Sync {
        ...省略源代码
    }

    /**
     * 实例化ReentrantLock 默认非公平的锁
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * 实例化ReentrantLock,设置是否为公平锁
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
2.2 功能方法

获取锁

    /**
     * 获取锁,失败则进入同步队列,并阻塞
     */
    public void lock() {
        sync.lock();
    }

    /**
     * 功能同lock(),阻塞响应中断
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     * 尝试获取锁,成功返回true,失败返回false
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     * 功能同lockInterruptibly(),阻塞可以设置超时
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

释放锁

 /**
     * 释放锁,同步队列前面的线程从阻塞中唤醒,在次尝试获取锁,成功返回,失败继续阻塞等待唤醒
     */
    public void unlock() {
        sync.release(1);
    }

其他

    /**
     * 获取条件队列
     */
    public Condition newCondition() {
        return sync.newCondition();
    }

    /**
     * 获取拥有锁的线程的重入次数
     */
    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();
    }
2.3 核心方法
2.3.1 FairSync(公平锁)
    /**
     * 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;
                }
            }
            /**  同步状态为1,表示锁被占用,判断占用锁的线程是否是当前线程,锁重入  **/
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
        ...省略代码
        
        /**
         * 释放锁
         */
        protected final boolean tryRelease(int releases) {
            /** 重新计算当前同步状态 **/
            int c = getState() - releases;
            /** 校验当前线程是否拥有锁 **/
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            /** 只能当同步状态为0时,重置exclusiveOwnerThread,释放同步状态成功 **/
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            /** 设置同步状态 **/
            setState(c);
            return free;
        }
2.3.2 非公平锁
    /**
     * NonfairSync(非公平锁) 继承 Sync 抽象类。
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * 非公平获取锁
         */
        final void lock() {
            /** 使用CAS 将同步状态设置为1,成功则将当前线程设置为exclusiveOwnerThread**/
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                /** 调用AQS 获取锁 **/
                acquire(1);
        }

        /**
         * 能否获取非公平锁
         */
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
        ...省略代码
        /**
         * 非公平锁获取同步状态逻辑
         *  返回true 表示获取同步状态成功
         *  返回false 表示获取同步状态失败
         */
        final boolean nonfairTryAcquire(int acquires) {
            /** 获取当前线程 **/
            final Thread current = Thread.currentThread();
            /** 获取父类同步状态 **/
            int c = getState();
            /** 同步状态为0 表示锁没有被占用 **/
            if (c == 0) {
                /** 使用CAS设置同步状态为1, **/
                if (compareAndSetState(0, acquires)) {
                    /** 将当前线程设置为exclusiveOwnerThread **/
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            /** 同步状态为1,表示锁被占用,判断占用锁的线程是否是当前线程,锁重入  **/
            else if (current == getExclusiveOwnerThread()) {
                /** 同步状态+1 **/
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                /** 设置同步状态 **/
                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;
            /** 只能当同步状态为0时,重置exclusiveOwnerThread,释放同步状态成功 **/
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            /** 设置同步状态 **/
            setState(c);
            return free;
        }

相关文章

网友评论

      本文标题:J.U.C锁之 ReentrantLock

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