万字超强图文讲解AQS以及ReentrantLock应用(建议收藏) (2)

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

Lock 是怎样起到锁的作用呢?

如果你熟悉 synchronized,你知道程序编译成 CPU 指令后,在临界区会有 moniterenter 和 moniterexit 指令的出现,可以理解成进出临界区的标识

从范式上来看:

lock.lock() 获取锁,“等同于” synchronized 的 moniterenter指令

lock.unlock() 释放锁,“等同于” synchronized 的 moniterexit 指令

那 Lock 是怎么做到的呢?

这里先简单说明一下,这样一会到源码分析时,你可以远观设计轮廓,近观实现细节,会变得越发轻松

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

其实很简单,比如在 ReentrantLock 内部维护了一个 volatile 修饰的变量 state,通过 CAS 来进行读写(最底层还是交给硬件来保证原子性和可见性),如果CAS更改成功,即获取到锁,线程进入到 try 代码块继续执行;如果没有更改成功,线程会被【挂起】,不会向下执行

但 Lock 是一个接口,里面根本没有 state 这个变量的存在:

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

它怎么处理这个 state 呢?很显然需要一点设计的加成了,接口定义行为,具体都是需要实现类的

Lock 接口的实现类基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的

那什么是队列同步器呢? (这应该是你见过的最强标题党,聊了半个世纪才入正题,评论区留言骂我)

队列同步器 AQS

队列同步器 (AbstractQueuedSynchronizer),简称同步器或AQS,就是我们今天的主人公

问:为什么你分析 JUC 源码,要从 AQS 说起呢?

答:看下图

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

相信看到这个截图你就明白一二了,你听过的,面试常被问起的,工作中常用的

ReentrantLock

ReentrantReadWriteLock

Semaphore(信号量)

CountDownLatch

公平锁

非公平锁

ThreadPoolExecutor (关于线程池的理解,可以查看 为什么要使用线程池? )

都和 AQS 有直接关系,所以了解 AQS 的抽象实现,在此基础上再稍稍查看上述各类的实现细节,很快就可以全部搞定,不至于查看源码时一头雾水,丢失主线

上面提到,在锁的实现类中会聚合同步器,然后利同步器实现锁的语义,那么问题来了:

为什么要用聚合模式,怎么进一步理解锁和同步器的关系呢?

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

我们绝大多数都是在使用锁,实现锁之后,其核心就是要使用方便

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

从 AQS 的类名称和修饰上来看,这是一个抽象类,所以从设计模式的角度来看同步器一定是基于【模版模式】来设计的,使用者需要继承同步器,实现自定义同步器,并重写指定方法,随后将同步器组合在自定义的同步组件中,并调用同步器的模版方法,而这些模版方法又回调用使用者重写的方法

我不想将上面的解释说的这么抽象,其实想理解上面这句话,我们只需要知道下面两个问题就好了

哪些是自定义同步器可重写的方法?

哪些是抽象同步器提供的模版方法?

同步器可重写的方法

同步器提供的可重写方法只有5个,这大大方便了锁的使用者:

万字超强图文讲解AQS以及ReentrantLock应用(建议收藏)

按理说,需要重写的方法也应该有 abstract 来修饰的,为什么这里没有?原因其实很简单,上面的方法我已经用颜色区分成了两类:

独占式

共享式

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyfypd.html