博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReentrantReadWriteLock读写锁及其在 RxCache 中的使用
阅读量:6691 次
发布时间:2019-06-25

本文共 5504 字,大约阅读时间需要 18 分钟。

一. 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地址:

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


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

转载地址:http://gueao.baihongyu.com/

你可能感兴趣的文章
Linux基础学习(3)--初学注意
查看>>
php总结笔记[转]
查看>>
jni开发中的常见错误
查看>>
【ZeroClipboard is not defined】的解决方法
查看>>
【题解】Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法
查看>>
iOS 一个ViewController上显示2个tableView的方法
查看>>
VSCode插件整理
查看>>
【六】tf和cgi进行联合试验,完成日志服务器
查看>>
面试题解(2):loop相关
查看>>
《当当网系统分级与海量信息动态发布实践》读后感
查看>>
完整的学习C++的读书路线图
查看>>
Android获取屏幕宽度、高度的4种方法
查看>>
Go学习笔记-使用MySQL数据库
查看>>
python asyncio异步代理池
查看>>
httpclient post请求实例(自己写的)
查看>>
记一次log4j日志文件输出错误的解决
查看>>
什么是PHP Guzzle?
查看>>
rabbitmq 不发送ack消息如何处理: RabbitMQ 消息确认以及消息消费方处理消息时候抛出了异常以...
查看>>
Bluefish
查看>>
Step by step guide to set up master and slave machines(转)
查看>>