深入理解AQS之ReentranLock源码分析

180 阅读3分钟

什么是AQS

JUC包中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列,条件队列,独占获取,共享获取,而这些行为的抽象就是基于AbstractQueueSynchronizer(AQS)实现的,即抽象队列同步器,采用模版方法模式封装共性的实现,可以用来实现一个依赖状态的同步器。

AQS具备的特性:

  1. 阻塞等待队列
  2. 共享/独占
  3. 公平/非公平
  4. 可重入
  5. 允许中断

JDK中提供的大多数的同步器如Lock, Latch, Barrier等,都是基于AQS框架来实现的

如何设计一把独占锁?

管程----Java同步的设计思想

  1. 管程:管理共享变量以及对共享变量的操作过程,让他们支持并发
  2. 互斥:同一时刻只允许一个线程访问共享资源
  3. 同步:线程之间如何进行通信、协作

MESA模型

在管程的发展史上,先后出现过三种不同的管程的模型,分别是Hasen模型、Hoare模型和MESA模型。现在正在广泛使用的是MESA模型。

image.png

管程中引入了条件变量的概念,而且每个条件变量都对应有一个等待队列。条件队列和等待队列的作用是解决线程之间的同步问题。

  • 同步等待队列是用来针对互斥问题的。
  • 条件等待队列,针对同步场景控制线程的顺序,没满足条件,让线程等待,满足条件从等待队列里面唤醒

Java中针对管程有两种实现:

  • 一种是基于Object的Monitor机制,用于sychronized内置锁的实现
  • 一种是抽象队列同步器AQS,用于juc包下的Lock锁机制

AQS核心结构

内部维护属性 volatile int state

  • state表示资源的可用状态

三种访问方式:

  • getState()
  • setState()
  • compareAndSetState()

定义了两种资源的访问方式:

  • 独占式---Exclusive:只有一个线程能够执行,如ReentrantLock
  • 共享式----Share:多个线程可以同时执行,如:CountDownLatch/Semophore

AQS实现时候主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它
  • tryAcquire(int):独占式方式,尝试获取资源,成功则返回true,失败则返回false
  • tryRelease(int):独占方式,尝试释放资源成功则返回true,失败则返回false
  • tryAcquireShared(int):共享方式,尝试获取资源。负数表示失败;0表示成功,但是没有剩余可用资源;正数表示成功,且有剩余资源
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待节点返回true,否则返回false.

2.3AQS定义两种队列

  • 同步等待队列:主要用于维护获取锁失败时入队的线程
  • 条件等待队列:调用await()的时候会释放锁,然后线程会加入到条件队列,等到某个条件获取后重新获得锁,调用signal()唤醒的时候会把条件队列中的线程节点移动到同步队列中,等待再次获取锁。

AQS源码分析.jpg