一. 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 系列的相关文章:

  1. 堆外内存及其在 RxCache 中的使用
  2. Retrofit 风格的 RxCache及其多种缓存替换算法
  3. RxCache 整合 Android 的持久层框架 greenDAO、Room
  4. 给 Java 和 Android 构建一个简单的响应式Local Cache

Java与Android技术栈:每周更新推送原创技术文章,欢迎扫描下方的公众号二维码并关注,期待与您的共同成长和进步。

转载于:https://juejin.im/post/5c5cf13a6fb9a049c84fe9e5

ReentrantReadWriteLock读写锁及其在 RxCache 中的使用相关推荐

  1. android读写锁,ReentrantReadWriteLock读写锁及其在 RxCache 中的使用

    一. ReentrantReadWriteLock读写锁 Lock 是相当于 synchronized 更面向对象的同步方式,ReentrantLock 是 Lock 的实现. 本文要介绍的 Reen ...

  2. 浅析ReentrantReadWriteLock读写锁

    在并发场景中用于解决线程安全的问题,我们会高频率的使用到独占式锁,通常使用java提供的关键字synchronized或者concurrents包中实现了Lock接口ReentrantLock.它们都 ...

  3. ReentrantReadWriteLock读写锁(读多写少场景)

    ReentrantReadWriteLock读写锁 适合读多写少的场景. 读锁ReentrantReadWriteLock.ReadLock可以被多个线程同时持有, 所以并发能力很高. 写锁Reent ...

  4. ReentrantReadWriteLock读写锁的使用

    Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...

  5. ReentrantReadWriteLock读写锁

    关注微信公众号JavaStorm获取最新文章. JavaStorm 概述 ​ ReentrantReadWriteLock是Lock的另一种实现方式,我们已经知道了ReentrantLock是一个排他 ...

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

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

  7. java并发-ReentrantReadWriteLock读写锁

    一.概念 Java常见的多是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞.读写锁维护了一对锁,一个读锁 ...

  8. ReentrantReadWriteLock——读写锁如何升级,为何读写锁不能插队?

    我们主要探讨读锁应该插队吗?以及什么是读写锁的升降级. 读锁插队策略:     首先,我们来看一下读锁的插队策略,在这里先快速回顾一下在 24 课时公平与非公平锁中讲到的 ReentrantLock, ...

  9. ReentrantReadWriteLock(读写锁)

    为了提高性能,java提供了读写锁, 读锁: 在读的地方使用读锁,可以多个线程同时读. 写锁: 在写的地方使用写锁,只要有一个线程在写,其他线程就必须等待 例子: public static Read ...

最新文章

  1. stm32f746 linux,在Linux系统下搭建STM32开发环境--Nucleo-F429ZI
  2. 2022版全球及中国电梯行业投资建议与盈利价值分析报告
  3. django orm 常用查询筛选
  4. Ansible剧本介绍及使用演示(week5_day2)--技术流ken
  5. 遍历删除_面试难题:List 如何一边遍历,一边删除?
  6. IOS工作笔记002---windows给VmWare虚拟机OS系统安装VMTools
  7. javascript中的对象之间继承关系
  8. selenium 点击后没反应未报错_Selenium代码迁移时会出现哪些问题?(附解决方案)...
  9. UE4之TextureSample
  10. 按键精灵文字识别插件_按键精灵课程学习目录
  11. Python中的while循环
  12. 基于Java的超市积分管理系统(附:论文 源码 课件)
  13. 彻底干掉霸占我任务栏的2345好压的垃圾广告搜索以及天气预告工具栏
  14. 考试 倒计时 php,PHP实现考试倒计时功能代码
  15. 【HTTP劫持和DNS劫持】
  16. 激光测距仪的发展与介绍——TFN 10K KI 双目远距离激光测距仪
  17. 计算机软件著作权保护包括哪些
  18. 企业微信群消息关键字提醒如何设置
  19. PO,BO,VO,DTO和POJO
  20. openssl enc 加密/解密文件

热门文章

  1. py编程技巧-1.5-如何快速找到多个字典的公共键(key)
  2. Windows Server 2008技术概述(自CSDN)
  3. ARM裸机篇---启动代码分析
  4. Linux各个发行版本的选择
  5. LinkedList源码(基础代码)
  6. [其他]JAVA与C#的Socket通信
  7. 如何用java完成Excel快速的导入导出
  8. Hibernate总结2 API和配置文件
  9. 第4章 管道与FIFO
  10. Share Point 2013使用Windows PowerShell 获取,删除UserProFile