AQS之ReentrantLock独占锁源码解析
一、基础概念
AQS(AbstractQueuedSynchronizer):是jdk并发包java.util.concurrent下绝大部分工具类实现的基础。
管程:是指管理共享变量以及对共享变量操作的过程,让它们支持并发
JVM层对管程的实现:synchronized
JAVA层对管程的实现:AQS抽象层,对AQS进行实现
JAVA的线程安全问题解决方案:cas+自旋(会出现空转cpu的问题)、synchronized、reentrantLock 会阻塞未获取锁的线程
----- JDK中提供的大多数的同步器如Lock,Latch,Barrier等,都是基于AQS框架来实现的,一般事通过一个内部类Sync继承AQS将同步器所有调用都映射到Sync对应的方法
二、AQS
特性:阻塞等待队列 共享/独占 公平/非公平 可重入 允许中断
AQS内部维护属性 volatile int state
表示同步的状态,对于独占锁而言,这个属性可以网上增加,每重入一次就+1;对于共享锁而言,就是资源个数,每来一个-1,当减为0以后,后续的资源就不能获取到锁了。
state三种访问方式
getState() setState() compareAndSetState()
AQS定义两种资源共享方式:
Exclusive 独占,只有一个线程能获取锁执行,如ReentrantLock
Share 共享 ,多个线程可以同时执行,如 Semaphore/CountDownLatch
AQS定义两种等待队列:
同步等待队列:主要用于维护获取锁失败时入队的线程
条件等待队列:调用await()的时候会释放锁,然后线程会加入到条件队列,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获得锁。
不同的自定义同步器竞争共享资源的方式也不同。自定义同步器在实现时只需要实现共享 资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:
- isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它
tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
同步等待队列 (双向链表结构)
基于双向链表数据结构的队列,是FIFO先进先出线程等待队列
条件等待队列
AQS中条件队列是使用单向列表保存的,用nextWaiter来连接: 调用await方法阻塞线程;
当前线程存在于同步队列的头结点,调用await方法进行阻塞(从同步队列转化到条 件队列)
自定义实现AQS:
实现加锁 解锁逻辑
public class MyselfLock extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int unused) {
//cas 实现加锁逻辑 默认 state =0 ,
if(compareAndSetState(0,1)){
//将当前的线程进行绑定,设置为一个独占的线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int unused) {
//释放锁
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock(){
acquire(1);
}
public boolean tryLock(){
return tryAcquire(1);
}
public void unlock(){
release(1);
}