在看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. SpringBoot开发十二-账号设置

    需求介绍-账号设置 账号设置里面的上传头像(文件) 首先请求必须是一个 POST 请求,其次表单的属性 enctype = "multipart/form-data" 然后就是利用 ...

  2. 解决vscode+python不提示numpy函数的问题

    前言 使用vscode编写numpy代码时,对于numpy.array()等方法总是无法提示.查找了很多博客后,大部分都是修改配置和安装多种vscode插件,经过尝试后方法对于我来说无效.最后在调试p ...

  3. sqli-labs lesson 11-15

    从这一关开始我们开始进入到post注入的世界了,什么是post呢?就是数据从客户端提交到服务器端,例如我们在登录过程中,输入用户名和密码,用户名和密码以表单的形式提交,提交到服务器后服务器再进行验证. ...

  4. 陀螺仪、加速度计与MPU6050的关系

    文章目录 一.引言 二.陀螺仪 三.加速度计 四.MPU6050 一.引言 要开始做俩轮平衡小车了,所以需要补充一些关于平衡小车的知识,首先就是如何让小车保持平衡,这是要依据小车当前的三轴角度和平衡位 ...

  5. SQL 练习29

    查询课程名称为「数学」,且分数低于 60 的学生姓名和分数 SELECT Student.Sname,Course.Cname,SC.score FROM Student,Course,SC WHER ...

  6. 题解 Connect

    传送门 各种骗分无果,特殊性质还手残写挂了-- 首先完全图上直接输出边权 \(\times (n-2)\) 就行了,然而我脑残乘的 \(n-1\) 看数据范围肯定是状压,但是压边肯定炸了,考虑压点 因 ...

  7. RHEL 7 “There are no enabled repos” 的解决方法

    RHEL 7 "There are no enabled repos"  的解决方法 [root@system1 Desktop]# yum install squidLoaded ...

  8. 转: SIFT原理解释

    1.SIFT概述 SIFT的全称是Scale Invariant Feature Transform,尺度不变特征变换,由加拿大教授David G.Lowe提出的.SIFT特征对旋转.尺度缩放.亮度变 ...

  9. visual studio如何检查内存泄露?

    Visual Studio有专门的插件叫做Visual Leak Detector (VLD)Visual Leak Detector for Visual C++ 2008/2010/2012/20 ...

  10. C++智能指针的原理和实现

    一.智能指针起因 在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete. new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针: delete:指向 ...