ReentrantReadWriteLock读写锁及其在 RxCache 中的使用
一. ReentrantReadWriteLock读写锁
Lock 是相当于 synchronized 更面向对象的同步方式,ReentrantLock 是 Lock 的实现。
本文要介绍的 ReentrantReadWriteLock 跟 ReentrantLock 并没有直接的关系,因为它们之间没有继承和实现的关系。
但是 ReentrantReadWriteLock 拥有读锁(ReadLock)和写锁(WriteLock),它们分别都实现了 Lock。
/** Inner class providing readlock */private final ReentrantReadWriteLock.ReadLock readerLock;/** Inner class providing writelock */private final ReentrantReadWriteLock.WriteLock writerLock;
复制代码
ReentrantReadWriteLock 在使用读锁时,其他线程可以进行读操作,但不可进行写操作。ReentrantReadWriteLock 在使用写锁时,其他线程读、写操作都不可以。ReentrantReadWriteLock 能够兼顾数据操作的原子性和读写的性能。
1.1 公平锁和非公平锁
从 ReentrantReadWriteLock 的构造函数中可以看出,它默认使用了非公平锁。
/*** Creates a new {@code ReentrantReadWriteLock} with* default (nonfair) ordering properties.*/public ReentrantReadWriteLock() {this(false);}/*** Creates a new {@code ReentrantReadWriteLock} with* the given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantReadWriteLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();readerLock = new ReadLock(this);writerLock = new WriteLock(this);}
复制代码
在 Java 中所谓公平锁是指,每个线程在获取锁时,会先查看此锁维护的等待队列,如果为队列空或者当前线程线程是等待队列的第一个,则占有锁。否则就会加入到等待队列中,以后按照 FIFO 的顺序从队列中取出。
非公平锁在获取锁时,不会遵循 FIFO 的顺序,而是直接尝试获取锁。如果获取不到锁,则像公平锁一样自动加入到队列的队尾等待。
非公平锁的性能要高于公平锁。
1.2 读锁
读锁是一个共享锁。读锁是 ReentrantReadWriteLock 的内部静态类,它的 lock()、trylock()、unlock() 都是委托 Sync 类实现。
Sync 是真正实现读写锁功能的类,它继承自 AbstractQueuedSynchronizer 。
1.3 写锁
写锁是一个排他锁。写锁也是 ReentrantReadWriteLock 的内部静态类,它的 lock()、trylock()、unlock() 也都是委托 Sync 类实现。写锁的代码类似于读锁,但是在同一时刻写锁是不能被多个线程所获取,它是独占式锁。
写锁可以降级成读锁,下面会介绍锁降级。
1.4 锁降级
锁降级是指先获取写锁,再获取读锁,然后再释放写锁的过程 。锁降级是为了保证数据的可见性。锁降级是 ReentrantReadWriteLock 重要特性之一。
值得注意的是,ReentrantReadWriteLock 并不能实现锁升级。
二. RxCache 中使用读写锁
RxCache 是一款支持 Java 和 Android 的 Local Cache 。目前,支持堆内存、堆外内存(off-heap memory)、磁盘缓存。
github地址:github.com/fengzhizi71…
RxCache 的 CacheRepository 类实现了缓存操作的类,它使用了 ReentrantReadWriteLock 用于保证缓存在读写时避免出现多线程的并发问题。
首先,创建一个读写锁,并获得读锁、写锁的实例。
class CacheRepository {private Memory memory;private Persistence persistence;private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();private final Lock readLock = lock.readLock();private final Lock writeLock = lock.writeLock();......
}
复制代码
在缓存的读操作时,使用读锁。
boolean containsKey(String key) {readLock.lock();try {if (Preconditions.isBlank(key)) return false;return (memory != null && memory.containsKey(key)) || (persistence != null && persistence.containsKey(key));} finally {readLock.unlock();}}
复制代码
在缓存的写操作时,使用写锁。
void remove(String key) {writeLock.lock();try {if (Preconditions.isNotBlank(key)) {if (memory != null) {memory.evict(key);}if (persistence != null) {persistence.evict(key);}}} finally {writeLock.unlock();}}
复制代码
对于某一个方法,如果在读操作做完之后要进行写操作,则需要先释放读锁,再获取写锁(否则会死锁)。写操作之后,还需要进行读操作的话,可以使用锁降级。
<T> Record<T> get(String key, Type type, CacheStrategy cacheStrategy) {readLock.lock();try {Record<T> record = null;if (Preconditions.isNotBlanks(key, type)) {switch (cacheStrategy) {case MEMORY: {if (memory!=null) {record = memory.getIfPresent(key);}break;}case PERSISTENCE: {if (persistence!=null) {record = persistence.retrieve(key, type);}break;}case ALL: {if (memory != null) {record = memory.getIfPresent(key);}if (record == null && persistence != null) {record = persistence.retrieve(key, type);if (memory!=null && record!=null && !record.isExpired()) { // 如果 memory 不为空,record 不为空,并且没有过期readLock.unlock(); // 先释放读锁writeLock.lock(); // 再获取写锁try {if (record.isNeverExpire()) { // record永不过期的话,直接保存不需要计算ttlmemory.put(record.getKey(),record.getData());} else {long ttl = record.getExpireTime()- (System.currentTimeMillis() - record.getCreateTime());memory.put(record.getKey(),record.getData(), ttl);}readLock.lock(); // 写锁在没有释放之前,获得读锁 (锁降级)} finally {writeLock.unlock(); // 释放写锁}}}break;}}}return record;} finally {readLock.unlock();}}
复制代码
三. 总结
ReentrantReadWriteLock 读写锁适用于读多写少的场景,以提高系统的并发性。因此,RxCache 使用读写锁来实现缓存的操作。
RxCache 系列的相关文章:
- 堆外内存及其在 RxCache 中的使用
- Retrofit 风格的 RxCache及其多种缓存替换算法
- RxCache 整合 Android 的持久层框架 greenDAO、Room
- 给 Java 和 Android 构建一个简单的响应式Local Cache
Java与Android技术栈:每周更新推送原创技术文章,欢迎扫描下方的公众号二维码并关注,期待与您的共同成长和进步。
转载于:https://juejin.im/post/5c5cf13a6fb9a049c84fe9e5
ReentrantReadWriteLock读写锁及其在 RxCache 中的使用相关推荐
- android读写锁,ReentrantReadWriteLock读写锁及其在 RxCache 中的使用
一. ReentrantReadWriteLock读写锁 Lock 是相当于 synchronized 更面向对象的同步方式,ReentrantLock 是 Lock 的实现. 本文要介绍的 Reen ...
- 浅析ReentrantReadWriteLock读写锁
在并发场景中用于解决线程安全的问题,我们会高频率的使用到独占式锁,通常使用java提供的关键字synchronized或者concurrents包中实现了Lock接口ReentrantLock.它们都 ...
- ReentrantReadWriteLock读写锁(读多写少场景)
ReentrantReadWriteLock读写锁 适合读多写少的场景. 读锁ReentrantReadWriteLock.ReadLock可以被多个线程同时持有, 所以并发能力很高. 写锁Reent ...
- ReentrantReadWriteLock读写锁的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- ReentrantReadWriteLock读写锁
关注微信公众号JavaStorm获取最新文章. JavaStorm 概述 ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他 ...
- 并发锁之二:ReentrantReadWriteLock读写锁
一.简介 读写锁是一种特殊的自旋锁,它把对共享资源对访问者划分成了读者和写者,读者只对共享资源进行访问,写者则是对共享资源进行写操作.读写锁在ReentrantLock上进行了拓展使得该锁更适合读操作 ...
- java并发-ReentrantReadWriteLock读写锁
一.概念 Java常见的多是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞.读写锁维护了一对锁,一个读锁 ...
- ReentrantReadWriteLock——读写锁如何升级,为何读写锁不能插队?
我们主要探讨读锁应该插队吗?以及什么是读写锁的升降级. 读锁插队策略: 首先,我们来看一下读锁的插队策略,在这里先快速回顾一下在 24 课时公平与非公平锁中讲到的 ReentrantLock, ...
- ReentrantReadWriteLock(读写锁)
为了提高性能,java提供了读写锁, 读锁: 在读的地方使用读锁,可以多个线程同时读. 写锁: 在写的地方使用写锁,只要有一个线程在写,其他线程就必须等待 例子: public static Read ...
最新文章
- stm32f746 linux,在Linux系统下搭建STM32开发环境--Nucleo-F429ZI
- 2022版全球及中国电梯行业投资建议与盈利价值分析报告
- django orm 常用查询筛选
- Ansible剧本介绍及使用演示(week5_day2)--技术流ken
- 遍历删除_面试难题:List 如何一边遍历,一边删除?
- IOS工作笔记002---windows给VmWare虚拟机OS系统安装VMTools
- javascript中的对象之间继承关系
- selenium 点击后没反应未报错_Selenium代码迁移时会出现哪些问题?(附解决方案)...
- UE4之TextureSample
- 按键精灵文字识别插件_按键精灵课程学习目录
- Python中的while循环
- 基于Java的超市积分管理系统(附:论文 源码 课件)
- 彻底干掉霸占我任务栏的2345好压的垃圾广告搜索以及天气预告工具栏
- 考试 倒计时 php,PHP实现考试倒计时功能代码
- 【HTTP劫持和DNS劫持】
- 激光测距仪的发展与介绍——TFN 10K KI 双目远距离激光测距仪
- 计算机软件著作权保护包括哪些
- 企业微信群消息关键字提醒如何设置
- PO,BO,VO,DTO和POJO
- openssl enc 加密/解密文件