zoukankan      html  css  js  c++  java
  • JUC锁框架源码阅读-ReentrantLock

    非公平锁

    并不会严格按照队列先后顺序获取锁,可能会出现插队

    阅读之前先要阅读《JUC锁框架源码阅读-AbstractQueuedSynchronizer》

    类图

    获取锁

     

    main

     public static void main(String[] args)  {
            //<1>创建锁 无参构造函数默认是非公平锁
            ReentrantLock reentrantLock=new ReentrantLock();
            //<2>加锁
            reentrantLock.lock();
        }

    1.非公平锁使用的是内部类ReentrantLock.NonfairSync的对象

    2.获取锁实现,直接CAS修改state状态为1(直接参与竞争锁 非公平体现)如果获取失败返回获取失败,AQS后续会加入CLH队列并阻塞等待唤醒

    <1>ReentrantLock构造函数

     public ReentrantLock() {
            //可以看到创建的是内部静态类NonfairSync
            sync = new ReentrantLock.NonfairSync();
        }

    <2>lock

    java.util.concurrent.locks.ReentrantLock.NonfairSync#lock

     final void lock() {
            /**
             * 调用尝试父类的CAS方法将 state从0设置为1 获取锁成功调用  这里就是非公平 可能出现插队的实现,不是放入对队列 而是直接参与获取
             *java.util.concurrent.locks.AbstractQueuedSynchronizer#compareAndSetState
             */
            if (compareAndSetState(0, 1))
                //<3>获取锁成功 调用父类方法设置当前持有锁线程 java.util.concurrent.locks.AbstractOwnableSynchronizer.setExclusiveOwnerThread
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //<4>调用父类的加锁方法acquire
                acquire(1);
        }

    <3>setExclusiveOwnerThread

    java.util.concurrent.locks.AbstractOwnableSynchronizer#setExclusiveOwnerThread

    protected final void setExclusiveOwnerThread(Thread thread) {
            exclusiveOwnerThread = thread;
        }

    <4>acquire

    我们通过《AQS源码阅读》 跟代码可以看到 tryAcquire是模板模式 最终还是子类自己实现的加锁逻辑

    public final void acquire(int arg) {
            /**
             *<5>tryAcquire  模板模式 是抽象方法由子类实现获取锁步骤
             *addWaiter   如果加锁失败 创建节点并放入队列尾
             *acquireQueued 通过新创建的节点 判断是重试获取锁。还是阻塞线程
             */
            if (!tryAcquire(arg) &&
                    acquireQueued(addWaiter(node.EXCLUSIVE), arg))
                //发出线程中断通知
                selfInterrupt();
        }

    <5>tryAcquire

    java.util.concurrent.locks.ReentrantLock.NonfairSync#tryAcquire

      protected final boolean tryAcquire(int acquires) {
            //<6>具体的加锁逻辑
            return nonfairTryAcquire(acquires);
        }

    <6>nonfairTryAcquire

    java.util.concurrent.locks.ReentrantLock.Sync#nonfairTryAcquire

     final boolean nonfairTryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取锁状态 父类方法 0表示还未被占用 大于0表示被占用
            int c = getState();
            if (c == 0) {
                //CAS尝试获取一次锁
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果大于0判断持有所得线程 是不是当前线程,因为锁是可重入的 可以重复获取
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

    指定时间的获取锁

    main

      //<>创建锁 无参构造函数默认是非公平锁
            ReentrantLock reentrantLock=new ReentrantLock();
            try {
    //<1>获取锁 reentrantLock.tryLock(
    1,TimeUnit.SECONDS); }finally { //<>释放锁 reentrantLock.unlock(); }

    1.如果获取失败返回获取失败

    2.AQS后续操作自旋并阻塞指定时间

    3.后续到了阻塞时间会再次自旋。判断到超时了则会直接返回false 终结自旋

    4.或者别的线程释放了锁。AQS唤醒了这个线程,自旋判断没有超时。则会继续超时获取锁如果获取失败继续阻塞指定时间

    <1>tryLock

    java.util.concurrent.locks.ReentrantLock#tryLock(long, java.util.concurrent.TimeUnit)

      public boolean tryLock(long timeout, TimeUnit unit)
                throws InterruptedException {
            //<2>调用的sync的 sync继承自父类的
            return sync.tryAcquireNanos(1, unit.toNanos(timeout));
        }

    <2>tryAcquireNanos

    java.util.concurrent.locks.AbstractQueuedSynchronizer#tryAcquireNanos

    可以阅读《AQS源码阅读》看后续流程

    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
                throws InterruptedException {
            //如果线程已经中断 直接抛出异常
            if (Thread.interrupted())
                throw new InterruptedException();
            //<5>or<4>调用子类实现尝试获取锁,如果没有获取到 则调用doAcquireNanos 阻塞指定时间
            return tryAcquire(arg) ||
                    doAcquireNanos(arg, nanosTimeout);
        }

    释放锁

    main

     public static void main(String[] args)  {
            //创建锁 无参构造函数默认是非公平锁
            ReentrantLock reentrantLock=new ReentrantLock();
            try {
                reentrantLock.lock();
            }finally {
                //<1>释放锁
                reentrantLock.unlock();
            }
        }

    1.首先根据父类的getExclusiveOwnerThread()当前持有锁线程 判断是不是当前线程获取 如果不是则报错

    4.释放前通过state-1 如果等于0才做具体释放逻辑(可重入逻辑)

    3.判断state是不是等于0如果等于0表示锁已经被释放 重复调用2次unlock。则清空持有锁线程为null setExclusiveOwnerThread(null); 返回true释放成功 后续交给AQS处理

    <1>unlock

    java.util.concurrent.locks.ReentrantLock#unlock

     public void unlock() {
            //<2>调用父类的release方法
            sync.release(1);
        }

    <2>release

    后续流程可以参考《AQS源码阅读》

    // 在独占锁模式下,释放锁的操作
        public final boolean release(int arg) {
            // <3>调用tryRelease子类方法,尝试去释放锁,由子类具体实现
            if (tryRelease(arg)) {
                Node h = head;
                // 如果队列头节点的状态不是0,那么队列中就可能存在需要唤醒的等待节点。
                // 还记得我们在acquireQueued(final Node node, int arg)获取锁的方法中,如果节点node没有获取到锁,
                // 那么我们会将节点node的前一个节点状态设置为Node.SIGNAL,然后调用parkAndCheckInterrupt方法
                // 将节点node所在线程阻塞。
                // 在这里就是通过unparkSuccessor方法,进而调用LockSupport.unpark(s.thread)方法,唤醒被阻塞的线程
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }

    <3>tryRelease

    java.util.concurrent.locks.ReentrantLock.Sync#tryRelease

      protected final boolean tryRelease(int releases) {
            //状态-1 大于0的数字表示可重入加了多少次锁
            int c = getState() - releases;
            //如果加锁线程非当前线程抛出异常
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //当c等于0表示最后一次调用unlock 则进行锁的释放
            if (c == 0) {
                free = true;
                //获得锁的线程设置为null
                setExclusiveOwnerThread(null);
            }
            //设置state
            setState(c);
            return free;
        }

    公平锁

    加锁

    严格按照阻塞队列获取,先等待先获取

    main

        public static void main(String[] args)  {
            //<1>创建锁 无参构造函数默认是非公平锁
            ReentrantLock reentrantLock=new ReentrantLock(true);
            //<2>加锁
            reentrantLock.lock();
        }

    1.公平锁 获取锁,会先判断CLH队列里面有没有数据,如果没有则表示没有等待线程 则直接尝试获取锁

    2.如果CLH有等待线程则直接加入CLH队列(公平锁的体现)

    <1>ReentrantLock

     public ReentrantLock(boolean fair) {
            //true的时候初始化的是FairSync 
            sync = fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync();
        }

    <2>lock

    java.util.concurrent.locks.ReentrantLock.FairSync#lock

       final void lock() {
            //<3>调用AQS的 acquire 
            acquire(1);
        }

    <3>acquire

    具体可以参考《AQS源码阅读》

    java.util.concurrent.locks.AbstractQueuedSynchronizer#acquire

    public final void acquire(int arg) {
            /**
             *<4>tryAcquire  模板模式 是抽象方法由子类实现获取锁步骤
             *addWaiter   如果加锁失败 创建节点并放入队列尾
             *acquireQueued 通过新创建的节点 判断是重试获取锁。还是阻塞线程
             */
            if (!tryAcquire(arg) &&
                    acquireQueued(addWaiter(node.EXCLUSIVE), arg))
                
                selfInterrupt();
        }

    <4>tryAcquire

    java.util.concurrent.locks.ReentrantLock.FairSync#tryAcquire

     protected final boolean tryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取锁状态
            int c = getState();
            //等于0表示 当前空闲状态可以尝试获取
            if (c == 0) {
                //<5>如果在CLH队列才尝试获取 否则返回false AQS放入CLH队列阻塞 (公平锁的实现)
                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;
        }

    <5>hasQueuedPredecessors

    AQS方法 遍历队列 判断当前线程是否在AQS队列中

    java.util.concurrent.locks.AbstractQueuedSynchronizer#hasQueuedPredecessors

      public final boolean hasQueuedPredecessors() {
            // The correctness of this depends on head being initialized
            // before tail and on head.next being accurate if the current
            // thread is first in queue.
            Node t = tail; // Read fields in reverse initialization order
            Node h = head;
            Node s;
            return h != t &&
                ((s = h.next) == null || s.thread != Thread.currentThread());
        }
  • 相关阅读:
    luoguP3822 [NOI2017]整数
    luoguP2150 [NOI2015]寿司晚宴
    luoguP3868 [TJOI2009]猜数字
    luoguP4777 【模板】扩展中国剩余定理(EXCRT)
    luoguP2048 超级钢琴
    题解 P1004 【方格取数】
    戊戌年西安游记
    题解 P4388 【付公主的矩形】
    题解 P4277 【河城荷取的烟花】
    001 dynamic Linq
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15206866.html
Copyright © 2011-2022 走看看