文章目录

  • 一、分析AQS锁
  • 二、自定义一把AQS锁
  • 三、分析ReentrantReadWriteLock锁源码
    • 1、Sync
    • 2、ReadLock
    • 3、WriteLock

一、分析AQS锁

在Java语言层面,它拥有自己的锁实现机制(JVM层面的是synchronized)。这个锁机制就是AbstractQueuedSynchronizer类,也就是我们常说的AQS。我们常见的ReentrantLock、CountDownLatch、Semaphore、CyclicBarrier等,都是基于AQS来实现的,只是不同的并发工具类使用到的是AQS的不同特性,如ReentrantLock加锁操作使用了互斥特性,Semaphore信号量就是利用了共享的特性,但两者都是在state变量上做文章。

AQS是一个抽象类,他无法直接实例化为一个对象,他只能依附于其他工具类的实现。AQS使用了一个模板方法的设计模式,它给出了我们实现锁的规范,我们只需要按照它的规范来,就可以完成对应的加锁、解锁逻辑。

不懂模板方法设计模式的可以参考我之前的博客:模板方法模式

本文仅讨论尝试加锁(tryAcquire)和尝试解锁(tryRelease)这两个方法。我认为这是比较核心的两个方法。
为什么我说这两个方法是比较核心的方法呢?我们以ReentrantLock为例,调用lock方法为起点。更详细的过程及源码分析,请参考浅谈AQS同步队列(含ReentrantLock加锁和解锁源码分析)此处仅进行流程分析。

1、选择调用ReentrantLock中的公平锁还是非公平锁实现(在创建ReentrantLock对象的时候,就会初始化出我们的锁对象,即可以指定是公平还是非公平的锁),我们以默认的非公平锁为例

2、然后调用当前类的acquire,而该类是AQS中实现的,即会调用父类方法

3、此时会调用对应的tryAcquire方法尝试获取锁

4、该方法在AQS中是一个会报错的实现,即具体实现方法只能是具体子类

5、此时就根据ReentrantLock中的方法逻辑进行获取锁

6、解锁流程逻辑同理

总结:AQS给出了锁的实现规则,具体的锁工具类,只需要实现对应的方法即可完成一把锁的功能


二、自定义一把AQS锁

总结上面的关键点:

1、lock方法为我们加锁的入口

2、tryAcquire为我们获取锁的逻辑

于是我们就可以自定义一把锁,完成锁的基本功能,代码如下:

public class MobianLock extends AbstractQueuedSynchronizer {// 用于加锁的入口public void lock(){acquire(1);}// 用于解锁的入口public void unlock(){release(1);}@Overrideprotected boolean tryAcquire(int arg) {// cas加锁 成功返回trueif(compareAndSetState(0,1)){setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int arg) {// 释放锁逻辑setExclusiveOwnerThread(null);setState(0);return true;}
}

测试结果:

锁效果成功


三、分析ReentrantReadWriteLock锁源码

ReentrantReadWriteLock锁的使用不再讲解

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁(读多写少)。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源(读读可以并发);但是如果一个线程想去写这些共享资源,就不应该允许其他线程对该资源进行读和写操作了(读写,写读,写写互斥)。在读多于写的情况下,读写锁能够提供比排它锁更好的并发性和吞吐量。

针对这种场景,JAVA的并发包提供了读写锁ReentrantReadWriteLock,它内部,维护了一对相关的锁,一个用于只读操作,称为读锁;一个用于写入操作,称为写锁,描述如下:

线程进入读锁的前提条件:

  • 没有其他线程的写锁
  • 没有写请求或者有写请求,但调用线程和持有锁的线程是同一个。

线程进入写锁的前提条件:

  • 没有其他线程的读锁
  • 没有其他线程的写锁

而读写锁有以下三个重要的特性:

  • 公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。
  • 可重入:读锁和写锁都支持线程重入。以读写线程为例:读线程获取读锁后,能够再次获取读锁。写线程在获取写锁之后能够再次获取写锁,同时也可以获取读锁。
  • 锁降级:遵循获取写锁、再获取读锁最后释放写锁的次序,写锁能够降级成为读锁。

ReentrantReadWriteLock的类图结构

根据ReentrantReadWriteLock类的情况可以两部分

  1. WriteLock和ReadLock为一部分:关键的读写锁逻辑
  2. Sync为一部分:维护锁的状态信息

1、Sync

直接继承AQS,在AQS的基础上,添加了一些要素

这里涉及了很多的位运算,其原因都要归咎于在ReentrantReadWriteLock锁的内部,它是将一个int类型的state分成16高位+16低位来处理的。

这个状态数字的判断,在后面的读写锁加锁过程中会经常使用到。

总结:高16位为读锁状态标志位,低16位为写锁状态标志位

2、ReadLock

读锁部分,依然可以分为lock和unlock方法,它们分别调用的是acquireShared和releaseShared方法。想都不用想,最终肯定是调用到tryAcquireShared和tryReleaseShared方法

public void lock() {sync.acquireShared(1);
}public void unlock() {sync.releaseShared(1);
}

加锁逻辑

解锁逻辑

3、WriteLock

同理,会调用到tryAcquire和tryRelease方法

public void lock() {sync.acquire(1);
}public void unlock() {sync.release(1);
}

加锁逻辑

解锁逻辑

浅谈AQS锁实现机制(含ReentrantReadWriteLock读写锁加锁解锁相关源码分析)相关推荐

  1. 并发锁之二:ReentrantReadWriteLock读写锁

    一.简介 读写锁是一种特殊的自旋锁,它把对共享资源对访问者划分成了读者和写者,读者只对共享资源进行访问,写者则是对共享资源进行写操作.读写锁在ReentrantLock上进行了拓展使得该锁更适合读操作 ...

  2. ue4中在物体上加ui_UE4 物体位置同步相关源码分析浅谈

    前言 多图, 不想在源代码写注释, 不想贴代码块, 看的不清楚 版本4.21混4.22, 区别不大 文章属于旧有文章搬运, 之前在csdn上面 2019.10.27修改一版 物体位置信息同步, 或者说 ...

  3. spring boot 源码分析(七) 事件机制 之 SpringApplicationEvent

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 前面的文章我们讲解了一下spring boot配置文件加载的相关源码分析,下面我们将从源码角度讲解一下spring  ...

  4. [转]浅谈MS-SQL锁机制

    本文转自:http://study.99net.net/study/database/mssql/1085625420.html 浅谈MS-SQL锁机制 2004-05-27     锁的概述 一. ...

  5. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  6. 你用对锁了吗?浅谈 Java “锁” 事

    每个时代,都不会亏待会学习的人 大家好,我是yes. 本来打算继续写消息队列的东西的,但是最近在带新同事,发现新同事对于锁这方面有一些误解,所以今天就来谈谈"锁"事和 Java 中 ...

  7. 搞懂分布式技术16:浅谈分布式锁的几种方案

    搞懂分布式技术16:浅谈分布式锁的几种方案 前言 随着互联网技术的不断发展,数据量的不断增加,业务逻辑日趋复杂,在这种背景下,传统的集中式系统已经无法满足我们的业务需求,分布式系统被应用在更多的场景, ...

  8. java 单例 读写锁_你用对锁了吗?浅谈 Java “锁” 事

    每个时代,都不会亏待会学习的人 大家好,我是yes. 本来打算继续写消息队列的东西的,但是最近在带新同事,发现新同事对于锁这方面有一些误解,所以今天就来谈谈"锁"事和 Java 中 ...

  9. 浅谈ASP.NET内部机制(五)

    浅谈ASP.NET内部机制(五) 前言:本章要谈页面生命周期了,过程挺多的,但是一点都不难.不信可以看看.我尽量的讲的平实一些,而且理解页面的生命周期对喜欢开发自定义控件和组件的朋友是很有帮助的. 系 ...

  10. 浅谈 LiveData 的通知机制

    LiveData 和 ViewModel 是 Google 官方的 MVVM 架构的一个组成部分.巧了,昨天分析了一个问题是 ViewModel 的生命周期导致的.今天又遇到了一个问题是 LiveDa ...

最新文章

  1. java maven项目使用sonar审核代码
  2. Shell 数组中 @ 跟 * 的区别
  3. apache+tomcat的架构
  4. MFC中静态文本控件显示的几种实现方式
  5. warnings (imported as 'THREE') was not found in 'three'
  6. 盲人如何学计算机编程,盲人程序员是如何编程的?
  7. 【Clickhouse】ClickHouse REST API(HTTP接口)及Engine引擎的使用
  8. 【python教程】-- 入门 | 小甲鱼《零基础入门学Python》教程笔记(知识点详细、源码可复制)全
  9. 图像分割法-snake
  10. 医院信息化建设,产品规划要求​
  11. Flink DataStream的Operator State、Keyed State、checkpoint、Savepoint、State Backends的使用和讲解
  12. 【英文SEO】Google网站流量分析
  13. Java网络爬虫Spider
  14. 基于OAS设计可扩展OpenAPI
  15. Android Studio 奇葩遭遇(xxx is never used)
  16. CUDA学习第三天:Kernel+grid+block关系
  17. RAID 1 的优点缺点和应用场景
  18. 任正非让寒气传递到每个人身上,互联网寒冬程序员该如何破冰?
  19. 迅睿CMS 程序安装教程
  20. conda安装tensorflow和conda常用命令

热门文章

  1. fetch jsonp连接mysql_后端接口开发:json,jsonp,restful
  2. 【Android自定义控件】圆圈交替,仿progress效果
  3. 使用matlab的appdesigner制作分析固定简单电路的APP
  4. [UESTC SC T2] 分解
  5. pwdx与netstat、lsof结合查找进程号是由哪个程序启动的
  6. linux 服务编程,Linux高性能服务编程(I/O复用)
  7. mysql1.0.17.0安装教程_mysql 8.0.17 安装配置图文教程
  8. 小米潘多拉路由器添加节点_小米mesh好用吗?AX3600AX1800 混组测试
  9. 滞后问题_富锂正极材料的电压滞后问题
  10. Eclipse2020+Tomcat9.0+Maven Web配置!