ReentrantLock

原来刨析过一遍ReentrantLock的源码;但是时间长了;底层原理和实现也记不太清了;所以,再深入看下源码


在创建ReentrantLock的实例的时候;可以传入指定的参数:true或者false;

ReentrantLock reelock=new  ReentrantLocktrue;

传入为true表示生成的为公平性锁;传入的为false表示生成的非公平性锁
ReentrantLook默认是非公平性锁

公平锁:按照线程的请求锁的顺序来获取锁,公平性锁
非公平锁:不是按照线程请求锁的顺序获取锁,而是抢占式获取锁

AQS介绍

ReentrantLock中有三个内部类:Sync、NonfairSync、FairSync
继承关系如下:

abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync{}

通过继承关系我们可以看出归根结底就是对 AbstractQueuedSynchronizer 的分析;因此我们先对AQS进行理解和分析:
AQS(AbstractQueuedSynchronizer)线程同步队列
在这里插入图片描述

基于FIFO(先进先出)队列,可以用于构建锁或者其他相关同步装置的基础框架。该同步器(以下简称同步器)利用了一个int来表示状态

同步器的开始提到了其实现依赖于一个FIFO队列,
那么队列中的元素Node就是保存着线程引用和线程状态的容器
每个线程对同步器的访问,都可以看做是队列中的一个节点。

Node的主要包含以下成员变量:
AQS的内部类Node {
    int waitStatus;//节点的状态
    Node prev;//前驱节点
    Node next;//后继节点
    Node nextWaiter;//存储condition队列中的后继节点
    Thread thread;//入队列时的当前线程
}

变量waitStatus则表示当前Node结点的等待状态,共有5种取值;其中包含的状态有:
CANCELLED,值为1,表示当前的线程被取消;
SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
值为0,表示当前节点在sync队列中,等待着获取锁。

AQS中的state:
state=0 表示锁是空闲状态
state>0 表示锁被占用
state<0 表示溢出

ReentrantLock加锁(公平,不公平)

注意:公平性锁,非公平性锁与可重入锁和不可重入锁是完全不同的概念

非公平性锁会创建,也就是new一个NonfairSync实例;
关于NofairSync这个内部类;加锁时调用的是这个类中的lock()方法

   static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
        //进行CAS操作尝试获取锁;CAS成功那么就把持有当前锁的线程设置为自己
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
                //如果CAS失败就调用acquire()方法,在下面介绍
            else
                acquire(1);
        }
        //在外部只调用lock加锁方法并不会执行下面的tryAcquire方法,这个方法是为上面的acquire方法调用的;实际是nofairTryAcquire()方法;在下面介绍
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

关于Acquire()方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

acquire方法中调用的tryAcquire(arg)方法实际上是同样调用了下面的 nonfairTryAcquire(int acquires)方法;先尝试获取锁,如果获取失败则将向等待队列中添加一个 waiter 结点。

非公性锁的关键代码:nonfairTryAcquire()方法

final boolean nonfairTryAcquire(int acquires) {
         //获取当前线程
        final Thread current = Thread.currentThread();
         //获取锁的状态
        int c = getState();
        //如果锁的状态为0,表示当前锁空闲,没有被占用
        if (c == 0) {
  //直接通过CAS获取锁,成功则将state置为1,设置拥有锁的线程为当前线程;直接返回
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //如果当前线程就是获取锁的那个线程
        else if (current == getExclusiveOwnerThread()) {
        //直接给state状态,也就是次数+1
            int nextc = c + acquires;
            //如果获取锁次数到达上限,直接抛出异常
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
                //更新state状态值;返回true
            setState(nextc);
            return true;
        }
   //走到这里说明当前锁被占用并且占用那个锁的线程不是当前线程;返回false,获取锁失败
        return false;
    }

非公平性锁加锁的流程

  1. 调用lock()方法,首先就进行CAS操作尝试获取锁;CAS成功那么就把持有当前持有锁的线程设置为自己
  2. 如果CAS失败就调用acquire(1);方法
  3. acquire()方法首先调用tryAcquire()方法尝试获取锁;这个方法本质上调用的是nonfairTryAcquire()方法
  4. nonFairTryAcuire()方法执行流程:首先获取当前线程和state值;如果state为0;不管AQS队列中前面有没有其他线程;直接通过CAS获取锁,成功则将state置为1,设置拥有锁的线程为当前线程;直接返回;如果当前线程是持有锁的那个线程;只需要把state状态+1进行更新即可;否则,说明当前锁被其他线程占用,返回false
  5. acquire()方法调用tryAcquire()方法返回false;说明获取锁失败,就加入阻塞队列中

公平性锁
调用公平性锁的方法:

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
//在外面调用枷锁方法,只会执行acquire(1);
    final void lock() {
        acquire(1);
    }

    /**
     * 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();
        //获取state值
        int c = getState();
        //如果state=0;表示当前没有线程持有锁
        if (c == 0) {
        //当前队列为空/当前线程处于队列的first;并且CAS成功;修改state=1并且标识当前线程为持有锁的线程,返回true
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //如果当前线程就是占用锁的线程,同非公平性锁一样;实现可重入锁的关键
        else if (current == getExclusiveOwnerThread()) {
        //直接给state状态,也就是次数+1
            int nextc = c + acquires;
             //如果获取锁次数到达上限,直接抛出异常
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            //更新state状态值;返回true
            return true;
        }
 //走到这里说明当前锁被占用并且占用那个锁的线程不是当前线程;返回false,获取锁失败
        return false;
    }
}

调用lock方法会执行acquire(1)方法;

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

if语句中的tryAcquire(arg)其实本质上是调用的 FairSync类里面提供的方法

公平性锁加锁的流程

  1. 调用lock()方法;与非公平性锁不同;公平性锁不进行CAS获取锁;而是直接调用acquire(1)方法
  2. acquire(1)方法最终调用的是tryAcquie()方法;
  3. tryAcquire()方法首先获取当前线程和state值;当state值为0时,判断在该线程前面是否还有正在排队的线程(这点与非公平性锁不同);如果没有正在排队的线程就进行CAS操作;成功就获取锁;如果有正在排队的线程,那么就不调用CAS
  4. 如果当前线程和持有锁的线程是同一个,那么直接将State的值+1;更新state即可

ReentrantLock释放锁

//释放锁操作
     protected final boolean tryRelease(int releases) {
              //每调用一次tryRelease方法;state的状态值就-1;
             int c = getState() - releases;
              if (Thread.currentThread() != getExclusiveOwnerThread()){
  //获取锁的线程非当前线程,也就是说根本没有获取锁怎么释放,逻辑不通,则直接抛出异常
                 throw new IllegalMonitorStateException();
                 }
             boolean free = false;
             if (c == 0) {
              //当state减为1的情况下,才真正释放锁,将持有锁的线程置为null
                 free = true;
                 setExclusiveOwnerThread(null);
             }
             //更新state的值
             setState(c);
             //返回free;true标识释放锁成功,false表示没有
             return free;
         }
 
     }

释放锁的流程:

  1. 调用一次tryRelease方法,state的状态值就-1;
  2. 当state减为1的情况下,才真正释放锁,将持有锁的线程置为null;标志位置为true;
  3. 否则就只更新state的值,不改变标志位,返回false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值