zoukankan      html  css  js  c++  java
  • ReentrantLock源码

    概述

      AQS框架下的锁都是实现Lock接口并实现tryAcquire方法,在tryAcquire方法中对state变量进行修改来改变锁的状态。

    • 重入性。获得锁的线程可以再次获得锁。
    • 公平锁。严格保证先尝试获得锁的线程能够先获得锁。
    • 非公平锁。不能严格保证先尝试获得锁的线程先获得锁。

    获得锁

    非公平锁

      cas修改state的状态,如果成功设置当前重入锁的拥有者是当前线程。cas对当前state的期待值为0,所以一个线程如果是重入自己持有的锁,在这一步是无法成功的。

      final void lock() {
                if (compareAndSetState(0, 1))
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    acquire(1);
            }

      如果cas失败会调用acquire(1),即调用tryAcquire(1),最终调用的是nonfairAcquire(1),该方法是非公平锁的核心实现。

    • 如果锁的state=0,cas修改,如果成功代表获得锁成功返回true;失败返回false,进入AQS的队列。
    • 如果state!=0但当前锁的线程拥有者是当前线程,进入锁的重入阶段,修改锁的state,记录所的重入次数,返回true代表线程成功获得锁。

      非公平重入锁的tryAcquire实现相对简单

    • 在锁的内部记录锁的持有线程,方便锁的重入
    • 用state的值记录锁的重入次数
     protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
     /**
             * Performs non-fair tryLock.  tryAcquire is implemented in
             * subclasses, but both need nonfair try for trylock method.
             */
            final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    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");
                    setState(nextc);
                    return true;
                }
                return false;
            }

    公平锁

      公平锁的lock方法中直接调用了acquire,而非公平锁lock方法中先cas,cas失败后才调用的acquire。这是公平性和非公平性的体现,acquire失败后会进入队列,AQS队列中的线程只有pre是head才可以尝试获得锁,即aqs通过队列的方式维护了公平性,如果把所有锁的获得都交给aqs去管理,那么锁就是公平的。相应的如果在加入aqs之前先自己尝试获取锁,那么这个锁就未必是公平的。例如:A线程获取锁的时候发现state不是0,失败进入AQS队列,B线程尝试获取锁的时候state恰好变成0,由于A线程在AQS队列尾部,A线程不具有争取锁的资格,所以B线程获得锁A没有获得锁,这就是非公平性的体现。  

     final void lock() {
                acquire(1);
            }

      这里同样分为两个部分

    • state==0代表锁空闲,但是要先判断AQS里是否有前继节点,如果有代表已经有人在排队,返回false进入AQS排队
    • 如果锁的持有着是当前线程,则重入并修改state
     /**
             * 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;
            }

    课代表划重点非公平锁和公平锁

    • 实现

    1. 非公平锁在lock方法中会先进行一次cas,cas成功当前线程就获取到锁,失败后才调用tryAcquire。公平锁没有先调用cas,而是调用acquire即调用tryAcquire。
    2. 在tryAcquire中,非公平锁只要当前state=0就可以进行cas,公平锁在state=0的时候还要先判断队列里是否有等待的线程,只有队列中没有等待的线程的情况下才会cas。
    3. 无论是公平锁还是非公平锁,才tryAcquire失败进入AQS队列后,AQS的先入先出的特性能够保证后续的公平性。
    • 比较

    1. 非公平锁容易出现饥饿。一个刚刚释放锁的线程再次获取锁的概率很大,所以会出现一个线程连续多次获得锁,造成饥饿。
    2. 公平锁效率低。公平性需要线程切换从而有许多没有必要的切换。

      

    释放锁

      公平锁和非公平锁的释放没有区别,都是静态内部类sync中的tryRelease方法。

    • 如果当前线程不是锁的持有线程,抛出异常。
    • 由于释放锁的线程一定是持有锁的线程,所以释放锁修改state不需要cas,直接相减。
    • 如果释放完毕后state=0,返回true,并设置当前所的持有者为null。
    • 如果释放完毕后state=0,返回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);
                }
                setState(c);
                return free;
            }

      

    其余获得锁的方法

      这些获得锁的方法都是公平锁和非公平锁没有区别。

    lockInterruptibly

      在获得锁失败被park时候能够相应中断,实现比较简单,直接调用AQS里的方法。

    tryLock

      非阻塞获取锁,类似NIO。如果lock没有被别的线程持有,那么tryLock会立即返回true,这种行为其实是一种非公平的锁,另外从它调用的方法nonfairTryAcquire也可以看出这一点。这也就意味着如果你申请了一个公平锁,调用一个公平锁的tryLock它的行为也是非公平的。如果lock此时被别的线程持有,tryLock会返回false。

        public boolean tryLock() {
            return sync.nonfairTryAcquire(1);
        }
            final boolean nonfairTryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                if (c == 0) {
                    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");
                    setState(nextc);
                    return true;
                }
                return false;
            }
  • 相关阅读:
    鸿蒙的js开发模式19:鸿蒙手机下载python服务器端文件的实现
    【资源下载】Linux下的Hi3861一站式鸿蒙开发烧录(附工具)
    并发编程大扫盲:带你了解何为线程上下文切换
    内存溢出 MAT 排查工具,它真香香香
    面试官问:如何排除GC引起的CPU飙高?我脱口而出5个步骤
    小学妹问我:如何利用可视化工具排查问题?
    你不得不知的6个JDK自带JVM调优工具
    那个小白还没搞懂内存溢出,只能用案例说给他听了
    听说同学你搞不懂Spring Boot集成Mybatis的玩法,田哥来教你
    手把手教你设置JVM调优参数
  • 原文地址:https://www.cnblogs.com/AshOfTime/p/10890108.html
Copyright © 2011-2022 走看看