在看canal源码时发现一个有趣的锁实现--BooleanMutex

这个锁在canal里面多处用到,相当于一个开关,比如系统初始化/授权控制,没权限时阻塞等待,有权限时所有线程都可以快速通过

先看它的核心基于AQS的锁实现:

private final class Sync extends AbstractQueuedSynchronizer {

        private static final long serialVersionUID = 2559471934544126329L;
/** State value representing that TRUE */
private static final int TRUE = 1;
/** State value representing that FALSE */
private static final int FALSE = 2; private boolean isTrue(int state) {
return (state & TRUE) != 0;
}
/**
* 实现AQS的接口,获取共享锁的判断
*/
protected int tryAcquireShared(int state) {
// 如果为true,直接允许获取锁对象
// 如果为false,进入阻塞队列,等待被唤醒
return isTrue(getState()) ? 1 : -1;
} /**
* 实现AQS的接口,释放共享锁的判断
*/
protected boolean tryReleaseShared(int ignore) {
// 始终返回true,代表可以release
return true;
} boolean innerState() {
return isTrue(getState());
} void innerGet() throws InterruptedException {
acquireSharedInterruptibly(0);
} void innerGet(long nanosTimeout) throws InterruptedException, TimeoutException {
if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException();
} void innerSetTrue() {
for (;;) {
int s = getState();
if (s == TRUE) {
return; // 直接退出
}
if (compareAndSetState(s, TRUE)) {// cas更新状态,避免并发更新true操作
releaseShared(0);// 释放一下锁对象,唤醒一下阻塞的Thread
return;
}
}
} void innerSetFalse() {
for (;;) {
int s = getState();
if (s == FALSE) {
return; // 直接退出
}
if (compareAndSetState(s, FALSE)) {// cas更新状态,避免并发更新false操作
return;
}
}
} }

它重写了AQS中共享锁的判断可持有方法tryAcquireShared和判断可释放锁的方法tryReleaseShared

也就是说,它是重写AQS中共享锁的方法来实现的,有意思的是它不像普通AQS实现的共享锁,比如Semaphore这种有许可证个数,也就是同时能被几个线程持有是固定的,而它理论上只要打开了开关就允许任意线程通过

下面看下它是怎么实现的:

首先它定义了两个int类型属性:

/** State value representing that TRUE */
private static final int TRUE = 1;
/** State value representing that FALSE */
private static final int FALSE = 2;

这两个属性代表了AQS中state的值,也就是说这把锁中的state值始终为1或者2,看下下面这两个方法

    void innerSetTrue() {
for (;;) {
int s = getState();
if (s == TRUE) {
return; // 直接退出
}
if (compareAndSetState(s, TRUE)) {// cas更新状态,避免并发更新true操作
releaseShared(0);// 释放一下锁对象,唤醒一下阻塞的Thread
return;
}
}
} void innerSetFalse() {
for (;;) {
int s = getState();
if (s == FALSE) {
return; // 直接退出
}
if (compareAndSetState(s, FALSE)) {// cas更新状态,避免并发更新false操作
return;
}
}
}

显而易见,这两个方法都自旋的CAS改变state的值,那这两个方法在哪里调用呢?

  public void set(Boolean mutex) {
if (mutex) {
sync.innerSetTrue();
} else {
sync.innerSetFalse();
}
}

这个方法封装了改变state值的方法,其实它就相当于我们常用锁的加锁和释放锁方法,原因在于它获取共享锁的判断:

protected int tryAcquireShared(int state) {
return isTrue(getState()) ? 1 : -1;
}   
private boolean isTrue(int state) {
return (state & TRUE) != 0;
}

由于state值始终为1或0,当state值为0时isTrue返回false,tryAcquireShared返回-1,AQS在共享锁的获取锁判断中如果tryAcquireShared<0代表共享锁许可证已经颁发完,后续申请锁的线程需要进入FIFO队列等待

相反如果state值为1,tryAcquireShared>0就直接获取锁对象

所以,当set(false)的时候,自旋的CAS把state值改成0,线程需要等待,相反set(true)就能获取锁成功

canal源码之BooleanMutex(基于AQS中共享锁实现)的更多相关文章

  1. 学习JUC源码(1)——AQS同步队列(源码分析结合图文理解)

    前言 最近结合书籍<Java并发编程艺术>一直在看AQS的源码,发现AQS核心就是:利用内置的FIFO双向队列结构来实现线程排队获取int变量的同步状态,以此奠定了很多并发包中大部分实现基 ...

  2. 【java集合框架源码剖析系列】java源码剖析之java集合中的折半插入排序算法

    注:关于排序算法,博主写过[数据结构排序算法系列]数据结构八大排序算法,基本上把所有的排序算法都详细的讲解过,而之所以单独将java集合中的排序算法拿出来讲解,是因为在阿里巴巴内推面试的时候面试官问过 ...

  3. 源码分析系列1:HashMap源码分析(基于JDK1.8)

    1.HashMap的底层实现图示 如上图所示: HashMap底层是由  数组+(链表)+(红黑树) 组成,每个存储在HashMap中的键值对都存放在一个Node节点之中,其中包含了Key-Value ...

  4. Java并发包源码学习系列:AQS共享式与独占式获取与释放资源的区别

    目录 Java并发包源码学习系列:AQS共享模式获取与释放资源 独占式获取资源 void acquire(int arg) boolean acquireQueued(Node, int) 独占式释放 ...

  5. 3D语音天气球(源码分享)——在Unity中使用Android语音服务

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 开篇废话: 这个项目准备分四部分介绍: 一:创建可旋转的"3D球":3 ...

  6. Hbase源码分析:Hbase UI中Requests Per Second的具体含义

    Hbase源码分析:Hbase UI中Requests Per Second的具体含义 让运维加监控,被问到Requests Per Second(见下图)的具体含义是什么?我一时竟回答不上来,虽然大 ...

  7. JAVA常用集合源码解析系列-ArrayList源码解析(基于JDK8)

    文章系作者原创,如有转载请注明出处,如有雷同,那就雷同吧~(who care!) 一.写在前面 这是源码分析计划的第一篇,博主准备把一些常用的集合源码过一遍,比如:ArrayList.HashMap及 ...

  8. java基础系列之ConcurrentHashMap源码分析(基于jdk1.8)

    1.前提 在阅读这篇博客之前,希望你对HashMap已经是有所理解的,否则可以参考这篇博客: jdk1.8源码分析-hashMap:另外你对java的cas操作也是有一定了解的,因为在这个类中大量使用 ...

  9. 第74讲:从Spark源码的角度思考Scala中的模式匹配

    今天跟随王老师学习了从源码角度去分析scala中的模式匹配的功能.让我们看看源码中的这一段模式匹配: 从代码中我们可以看到,case RegisterWorker(id,workerHost,.... ...

随机推荐

  1. KNeighborsClassifier()的参数无效错误

    KNeighborsClassifier()的参数无效错误 写在前面:如果对自己英语拼写很自信,可以不用往下看了,我就是拼写错误 错误提示 Invalid parameter n_neightbors ...

  2. 最详细之教你Jenkins+github自动化部署.Net Core程序到Docker

    环境 centos7.9,.NET5 一.Jenkins搭建 1)下载Jenkins的war包 在\home目录建一个jenkins目录放jenkins的包 #进入\home目录 cd \home # ...

  3. SQL Server 判断数据库中是否存在表

    使用场景 可以反复的执行相同脚本 方式1:查询sysobjects表 if EXISTS (SELECT * from sysobjects WHERE name='test_table') DROP ...

  4. noip28

    东方专场? T1 %%%WYZG 话说我考场上还想二维hash来着 考虑只记录弹幕中x的相对位置. 先选定弹幕一个点作为基准点(第一个出现的x即可),然后,枚举其他的x,记录下坐标差,然后去方格图中枚 ...

  5. NOIP 模拟 $14\; \text{队长快跑}$

    题解 \(by\;zj\varphi\) 一道很妙的 \(dp\) 题,方程状态不好设置,细节也不少 看到数据范围,直接想离散化 设 \(f_{i,j}\) 表示处理完前 \(i\) 个水晶,其中摧毁 ...

  6. 题解 Merchant

    传送门 可以发现如果我们最终选择的物品集合已经确定,就很好求了 \(\sum k*t+\sum b \geqslant s\) ,二分即可 但现在我们无法确定该选哪些物品 因此我们只需要check一下 ...

  7. SSM自学笔记(一)

    本文内容 Ioc和DI Spring快速入门 Spring配置文件 Spring IoC和DI注解开发 Spring配置数据源 Spring注解开发 Spring整合Junit IoC 和 DI 1. ...

  8. Specification排序orderby

    废话不多说直接贴代码 Specification<Course> sf = new Specification<Course>() { @Override public Pre ...

  9. 面向对象之编写驱动程序--中断(linux系统、s3c6410开发板)

    /*------------------------- *先申明下,本人是个菜鸟,刚开始接触驱动程序编写,交代下开发环境(主机系统redhat6.3,开发板ARM-s3c6410) 以watchdog ...

  10. Hibernate之持久化对象

    时间:2017-1-19 23:00 --Hibernate持久化类的状态1.持久化类:就是一个实体类与数据库建立了映射.2.Hibernate为了方便管理持久化类,将持久化类分为了三种状态:    ...