一. 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)、磁盘缓存。

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();

}

}

对于某一个方法,如果在读操作做完之后要进行写操作,则需要先释放读锁,再获取写锁(否则会死锁)。写操作之后,还需要进行读操作的话,可以使用锁降级。

Record get(String key, Type type, CacheStrategy cacheStrategy) {

readLock.lock();

try {

Record 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永不过期的话,直接保存不需要计算ttl

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

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

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

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

  2. ReentrantReadWriteLock读写锁

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

  3. Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析

    文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...

  4. 并发编程-19AQS同步组件之重入锁ReentrantLock、 读写锁ReentrantReadWriteLock、Condition

    文章目录 J.U.C脑图 ReentrantLock概述 ReentrantLock 常用方法 synchronized 和 ReentrantLock的比较 ReentrantLock示例 读写锁R ...

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

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

  6. java 可重入读写锁 ReentrantReadWriteLock 详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...

  7. 浅析ReentrantReadWriteLock读写锁

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

  8. java锁包读写锁_Java并发包7--读写锁ReentrantReadWriteLock的实现原理解析

    前言 之前分析的ReentrantLock以及Synchronized都是排他锁,同一时间只允许一个线程进行资源的访问,但是有时需要允许多线程对资源进行读访问,而不允许多线程对资源写访问时,Reent ...

  9. 深入分析实战可重入读写锁ReentrantReadWriteLock

    文章目录 前言 加锁规则 同步原理 源码解析 实战演示 前言 前面我们学习了可重入锁ReentrantLock,可重入锁是一个排他锁,只要不是当前线程访问加锁资源都不能够进入,只能等待锁的释放.当然, ...

最新文章

  1. java字符串去掉空格
  2. 数据库范式(1NF、2NF、3NF、BCNF)
  3. springMVC(一) --前端控制器(DispatcherServlet)的作用
  4. 对校招生培养工作的建议_如何提升人才培养质量?西华的老师们正面临一场大考...
  5. 设计模式(十)------23种设计模式(3):抽象工厂模式
  6. mysql耦合_内聚与耦合
  7. 系统架构设计之-任务调度系统的设计
  8. java基于springboot+vue的校园一卡通管理系统 ssm nodejs
  9. 湖南大学ACM——10015.self-numbers
  10. SSM+基于微信小程序的航空售票管理系统 毕业设计-附源码191111
  11. sudoku_solver :数独解题器
  12. stm32定时器的ETR、CH1N、CH2N、CH3N
  13. js一键复制并调起微信客户端
  14. 治疗口腔溃疡的穴位按摩方法
  15. Python初级学习教程—从入门开始学习(函数、组合数据类型、文件操作、异常、模块)
  16. 【负荷预测、电价预测】基于神经网络的负荷预测和价格预测(Matlab代码实现)
  17. SDWebImage 图片加载失败
  18. visionpro(用visionpro对相机进行标定)
  19. nginx-2-讲解与使用
  20. Python3爬虫——利用百度翻译实现中文翻译英文

热门文章

  1. 如何学习和改进工业设计手绘
  2. mysql报错502_Nginx+uWSGI+Django 502 Bad Gateway错误
  3. oracle scn与数据恢复,基于scn的数据恢复
  4. 网络流——最大流(全)
  5. 小红书违规会限流多久?严重违规会被封号吗
  6. 英雄联盟手游赛事非常火爆电竞从业者有何不同
  7. Linux whereis、find和locate命令找不到文件
  8. 急性肠胃炎经常找上门?多数是因为冰箱里的不洁食物所致
  9. 微信红包(C++、Python)
  10. RPA这股风越来越大,数字雇员时代全面到来?