Java读写锁(ReentrantReadWriteLock)学习
什么是读写锁
平时,我们常见的synchronized和Reentrantlock基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,哪怕是读操作。而读写锁是维护了一对锁(一个读锁和一个写锁),通过分离读锁和写锁,使得同一时刻可以允许多个读线程访问,但是在写线程进行访问时,所有的读线程和其他写线程均被阻塞。
读写锁的优点
1. 简化了读写交互场景编程的复杂度:
在常见的开发中,我们经常会定义一个共享的用作缓存的数据结构;比如一个大Map,缓存全部的城市Id和城市name对应关系。这个大Map绝大部分时间提供读服务(根据城市Id查询城市名称等);而写操作占有的时间很少,通常是在服务启动时初始化,然后可以每隔一定时间再刷新缓存的数据。但是写操作开始到结束之间,不能再有其他读操作进来,并且写操作完成之后的更新数据需要对后续的读服务可见。
在没有读写锁支持的时候,如果需要完成上述工作就要使用Java的等待通知机制,就是当写操作开始时,所有晚于写操作的读操作均会进入等待状态,只有写操作完成并进行通知之后,所有等待的读操作才能继续执行(多个写操作之间依靠synchronized关键字进行同步),这样做的目的是使读操作能读取到正确的数据,不会出现脏读。改用读写锁实现上述功能,这只需要在读操作时获取读锁,写操作时获取写锁即可。当写锁被获取到时,后续(非当前操作线程)的读写锁都会被阻塞,写锁释放之后,所有操作继续执行,编程方式相对于使用等待通知机制的实现方式而言。变得简单明了。
2.扩大了程序的吞吐量:
读写锁通过读写使用不同的锁,读操作使用共享锁,写操作使用独占锁的形式,让程序能提供更好的并发性和吞吐量。在读多于写的情况下,读写锁能够提供比排它锁更好的性能。
ReentrantReadWriteLock的特性
特性 | 说明 |
---|---|
公平性选择 | 支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平 |
重进入 | 该锁支持重进入,以读写线程为例:读线程在获取了读锁之后,能够再次获取读锁。而写线程在获取了写锁之后能够再次获取写锁,同时也可以获取读锁 |
锁降级 | 允许从写锁降级为读锁,实现方式时:先获取写锁,再获取读锁,最后释放写锁,此时就会将为读锁。但是,从读锁升级到写锁时不可能的 |
Condition支持 | 写锁提供了一个Condition实现,对于写锁来说,该实现的行为与ReentrantLock.newCondition()提供的Condition实现对ReentrantLock所做的行为相同。当然,此Condition只能用于写锁。读锁不支持Condition,readLok().newCondition()会抛出UnsupportedOperationException。 |
监测 | 这些方法主要用于监听系统状态,而不是同步控制 |
使用案例
public class ReentrantReadWriteLockTest {
private static Map<String, Object> map = new HashMap<>();
private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private static Lock readLock = rwl.readLock();
private static Lock writeLock = rwl.writeLock();
public static void main(String[] args) {
ReentrantReadWriteLockTest reentrantReadWriteLockTest = new ReentrantReadWriteLockTest() ;
Executor executor = Executors.newFixedThreadPool(4) ;
executor.execute(() -> {
System.out.println("写锁开始");
reentrantReadWriteLockTest.put("1","1") ;
System.out.println("写锁结束");
});
executor.execute(() -> {
System.out.println("读锁开始");
System.out.println(reentrantReadWriteLockTest.get("4"));
System.out.println("读锁结束");
});
executor.execute(() -> {
System.out.println("写锁开始");
reentrantReadWriteLockTest.put("3", "3") ;
System.out.println("写锁开始");
});
executor.execute(() -> {
System.out.println(reentrantReadWriteLockTest.get("1"));
});
}
public void put(String key , String value) {
try {
writeLock.lock();
map.put(key, value);
}finally {
writeLock.unlock();
}
}
public Object get(String key) {
try {
readLock.lock();
return map.get(key);
}finally {
readLock.unlock();
}
}
}
Java读写锁(ReentrantReadWriteLock)学习的更多相关文章
- Java并发指南10:Java 读写锁 ReentrantReadWriteLock 源码分析
Java 读写锁 ReentrantReadWriteLock 源码分析 转自:https://www.javadoop.com/post/reentrant-read-write-lock#toc5 ...
- 轻松掌握java读写锁(ReentrantReadWriteLock)的实现原理
转载:https://blog.csdn.net/yanyan19880509/article/details/52435135 前言 前面介绍了java中排它锁,共享锁的底层实现机制,本篇再进一步, ...
- [图解Java]读写锁ReentrantReadWriteLock
图解ReentrantReadWriteLock 如果之前使用过读写锁, 那么可以直接看本篇文章. 如果之前未使用过, 那么请配合我的另一篇文章一起看:[源码分析]读写锁ReentrantReadWr ...
- 源码分析— java读写锁ReentrantReadWriteLock
前言 今天看Jraft的时候发现了很多地方都用到了读写锁,所以心血来潮想要分析以下读写锁是怎么实现的. 先上一个doc里面的例子: class CachedData { Object data; vo ...
- Java并发(十):读写锁ReentrantReadWriteLock
先做总结: 1.为什么用读写锁 ReentrantReadWriteLock? 重入锁ReentrantLock是排他锁,在同一时刻仅有一个线程可以进行访问,但是在大多数场景下,大部分时间都是提供读服 ...
- java 可重入读写锁 ReentrantReadWriteLock 详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt206 读写锁 ReadWriteLock读写锁维护了一对相关的锁,一个用于只 ...
- 从火车站车次公示栏来学Java读写锁
Java多线程并发之读写锁 本文主要内容:读写锁的理论:通过生活中例子来理解读写锁:读写锁的代码演示:读写锁总结.通过理论(总结)-例子-代码-然后再次总结,这四个步骤来让大家对读写锁的深刻理解. 本 ...
- 读写锁ReentrantReadWriteLock:读读共享,读写互斥,写写互斥
介绍 DK1.5之后,提供了读写锁ReentrantReadWriteLock,读写锁维护了一对锁:一个读锁,一个写锁.通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升.在读多写少的情况下, ...
- Java读写锁
Java读写锁,ReadWriteLock.java接口, RentrantReadWriteLock.java实现.通过读写锁,可以实现读-读线程并发,读-写,写-读线程互斥进行.以前面试遇到一个问 ...
随机推荐
- HDOJ(HDU).1059 Dividing(DP 多重背包+二进制优化)
HDOJ(HDU).1059 Dividing(DP 多重背包+二进制优化) 题意分析 给出一系列的石头的数量,然后问石头能否被平分成为价值相等的2份.首先可以确定的是如果石头的价值总和为奇数的话,那 ...
- ucenter搭建
使用xftp传到虚拟机.解压[root@ygy130 ~]# unzip -o -d ./Ucenter_1.6 UCenter_1.6.0_SC_UTF8.zip [root@ygy130 ~]# ...
- angularJS 条件查询 品优购条件查询品牌(条件查询和列表展示公用方法解决思路 及 post请求混合参数提交方式)
Brand.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l ...
- 软银开放Pepper开发,给机器人写安卓App是怎样一种体验?
日本软银推出的Pepper智能机器人 新浪科技讯 北京时间5月19日下午消息,谷歌Android移动操作系统的触角正在不断扩大,从今天开始,开发者可以为日本电信公司软银的Pepper人形机器人设计An ...
- Codeforces Round #307 (Div. 2) D 矩阵快速幂+快速幂
D. GukiZ and Binary Operations time limit per test 1 second memory limit per test 256 megabytes inpu ...
- Star sky 二维前缀和
C. Star sky time limit per test 2 seconds memory limit per test 256 megabytes input standard input o ...
- [技巧篇]12.从Spring的编码过滤器说起
有一枚学生问问了我一个问题,突然灵感爆发,他使用的Spring的过滤器,前台利用GET方式向后端发出一个请求,由于里面含有中文数据,结果在后端显示的是乱码,他问我为什么?明明在Spring里面也配了字 ...
- 使用 log4j 在控制台 打印 hibernate 语句参数
log4j.rootLogger=INFO, stdoutlog4j.appender.stdout=org.apache.log4j.ConsoleAppenderlog4j.appender.st ...
- redis的安装和常用命令
一.redis的安装 1.windows安装redis 下载地址:https://github.com/MSOpenTech/redis/releases. Redis 支持 32 位和 64 位.这 ...
- Gradle加载本地jar包
有时,我们需要的jar包不一定能在远程仓库中找到,这时我们需要加载本地的jar包. 加载单独的jar包 在项目底下添加libs目录,将jar包仍进libs目录 build.gradle配置如下: de ...