AQS和ReentrantLock源码剖析

AbstratQuenedSynchronizer抽象队列同步器

一. 简单理解

源码讲解视频
博客
在这里插入图片描述

核心参数

  • 变量state :没加锁state=0,加锁state=1
  • 加锁线程:CAS 设置state=1

加锁过程

  1. 尝试state的值由0变为1,如果发现state的值已经不为0了而是1,说明有线程加锁了
  2. 如果加锁失败,则放入等待队列中,等待其他线程释放锁

释放锁过程

  1. 将state递减为0,如果是可重入锁,例state=2,则需递减到0才会释放锁
  2. 将加锁线程set为null

可重入锁

判断加锁线程是否是当前线程(记录线程id)
ReentrantLcok、Synchronized
可以多次加锁lock和释放锁unlock

二. 源码剖析

源码讲解

1.核心参数

  1. int state
    ReentrantLcok、ReentrantReanWriteLock获取锁的方式是通过修改state变量
    CountDownLanch的计时器和Semaphore的信号量也是使用的state

  2. 双向链表 (未获取锁的线程)
    如果ReentrantLcok没拿到锁资源,则会将线程封装成Node存入,Node保存当前线程
    为什么这里使用双向链表?
    如果准备获取锁资源,需要出队,双向链表能够更好的修改头尾节点的指向
    双向链表初始化thread=null的节点,如果有新Node加入,(1)则优先修改Node自身的指向,指向为上一个节点(先操作私有变量,再操作共享变量volatile,保住原子性),(2 )让尾节点tail指向当前Node(3)让上一节点指向当前节点(存在多节点竞争,需要CAS加锁操作)
    走321,需要在多加一次锁
    在这里插入图片描述

  3. 单向链表(存wait挂起线程)
    waitSet
    存储ReentrantLcok使用await挂起的线程
    存储Synchronized 使用wait挂起的线程
    exq、EntryLis
    存储没获取锁资源的线程

2.ReentrantLock

abstract抽象类Sync继承了AQS
在这里插入图片描述
(1)ReentrantLock构造器

ReentrantLock默认无参构造非公平锁

	public ReentrantLock() {
        sync = new NonfairSync();
	}

有参构造,入参true公平锁new FairSync(),false非公平锁new NonFairSync()

	public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
	}

(2)lock()
抽象类Syns有两个实现类NonfairSync和FairSync,所以ReentrantLock有非公平锁和公平锁两种实现,所以lock()方法也有两种

  • 非公平锁NonfairSync的lock()
    直接使用CAS,尝试将state由0改为1(CAS:同一时间多个线程执行,只有一个能成功),如果失败则走acquire(1)
	final void lock() {
      	if (compareAndSetState(0, 1))
           setExclusiveOwnerThread(Thread.currentThread());
        else
           acquire(1);
    }
  • 公平锁FairSync的lock()
    acquire(1)
	final void lock() {
            acquire(1);
	}

(3)acquire()方法 拿锁
acquire直接使用的AQS的方法,不区分公平锁、非公平锁实现

        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  1. tryAcquire 再次拿锁,有公平锁、非公平锁两种实现
  2. addWaiter 没拿到锁,等待
  3. acquireQueued 挂起线程和后续被换线获取锁资源

acquire()的非公平锁实现nonfairTryAcquire(),尝试插队获取锁,tryLock()也使用的该方法

final boolean nonfairTryAcquire(int acquires) {
	//获取当前线程对象
     final Thread current = Thread.currentThread();
    //获取state
     int c = getState();
    //如果锁没用被有其他线程获取,则可以尝试获取
     if (c == 0) {
     	//非公平锁直接CAS尝试将state从0改为1
         if (compareAndSetState(0, acquires)) {
         	//成功拿到锁,并将当前线程设置为加锁线程
             setExclusiveOwnerThread(current);
             return true;
         }
     }
     //如果state!=1,判断当前线程是否为加锁线程
     else if (current == getExclusiveOwnerThread()) {
     	//如果是加锁线程,则锁重入,state+1
         int nextc = c + acquires;
         //防止int类型溢出,一般不会出现,可能先内存溢出
         if (nextc < 0) // overflow
             throw new Error("Maximum lock count exceeded");
         setState(nextc);
         return true;
     }
     return false;
 }

非阻塞锁tryLock()复用该方法nonfairTryAcquire()

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

acquire()的公平锁实现tryAcquire(),排队获取锁

protected final boolean tryAcquire(int acquires) {
 final Thread current = Thread.currentThread();
 int c = getState();
 if (c == 0) {
 	//判断是否有其他线程在等待,如果没用则获取锁,如果需要排队则放弃
 	//如果当前线程排在第一位则抢锁(因为被换线线程也会执行tryAcquire方法,这里是为了考虑此情况)
     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;
}

(4)addWaiter(Node.EXCLUSIVE), arg)
将未获取到锁的线程,封装为Node,添加到双向链表

3.CAS unsafe.compareAndSwapInt

tryAcquire()方法中compareAndSetState()方法

if (compareAndSetState(0, acquires)) {
	setExclusiveOwnerThread(current);
	return true;
}

底层是native修饰的Unsafe工具类的compareAndSwapInt(),使用CAS指令修改(硬件层面支持)

  1. this:当前对象
  2. stateOffset:获取state内存偏移量
  3. expect:state的值
  4. update:需要修改的state的值,update=1
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

获取当前AQS类下state属性在内存中的偏移量,也就是获取state属性的地址,因为地址是长整形所以用long接收

static final long stateOffset= unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
  • 13
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值