ReentrantReadWriteLock读写锁实现分析
排他锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻允许多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程均被阻塞。读写锁内部维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大的提升。
读写锁除了保证写操作对读操作的可见性和提高并发的性能之外,读写锁能够简化读写交互场景的编程方式。假设在程序中定义一个用作缓存的共享的数据结构,它的大部分时间提供读取服务,而写操作占有的时间非常的少,但是写操作完成之后的更新需要对后续的读服务可见。在没有读写锁支持的时候(JAVA 5之前),要完成上述工作就要使用Java的等待/通知机制,就是当写操作开始的时候,所有晚于写操作的读操作均会进入等待状态,只有写操作完成并进行通知之后,所有等待的读操作才能继续执行(写操作之间依靠synchronized进行同步),这样做的目的是使读操作能读取到正确的数据,不会出现脏读。改用读写锁实现上述功能,只需要在读操作时候获取读锁,写操作时候获取写锁,当写锁被获取到时,后续的(非当前写操作线程)所有读写操作都会被阻塞,在写锁释放之后,所有操作继续执行,编程方式相对于使用等待通知机制的实现方式变得简单明了。
Java并发包提供读写锁的实现是ReentrantReadWriteLock,它提供如下特性:
(1)公平性选择,支持非公平锁和公平锁的获取方式。
(2)重进入,支持重进入,线程获取读锁之后该线程能继续获取读锁;线程获取写锁之后能继续获取写锁,也可以获取读锁。
(3)锁降级,遵循获取写锁、获取读锁在释放写锁的次序,写锁能够降级成为读锁。
下面通过一个缓存示例说明读写锁的使用方式,示例代码如下:
public class Cache {
private static final Map<String,Object> cache = new HashMap<String,Object>() ;
private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock() ;
private static Lock r = lock.readLock() ;
private static Lock w = lock.writeLock();
public static final Object get(String key){
r.lock();
try {
return cache.get(key) ;
}finally{
r.unlock();
}
}
public static final Object put(String key,Object val){
w.lock();
try{
return cache.put(key, val);
}finally{
w.unlock();
}
}
public static final void clear(){
w.lock();
try{
cache.clear();
}finally{
w.unlock();
}
}
public static final Object remove(String key){
w.lock();
try{
return cache.remove(key) ;
}finally{
w.unlock();
}
}
}
上述的Cache组合一个非线程安全的HashMap作为缓存的实现,同时使用读写锁的读锁和写锁来保证Cache是线程安全的。在读操作get(String key)的方法中,需要获取读锁,这使得所有的读并发都不会被阻塞。写操作put(String key,Object val)方法和clear()和remove(String key)方法,在更新HashMap时必须提前获取写锁,当获取写锁之后,其他线程对于写锁和读锁的获取都将会被阻塞,而只有写锁释放之后其他读写操作才能够继续。Cache使用读写锁提升了读并发的性能,也保证了每次写操作对所有的读写操作的可见性,同时简化了编程方式。
读写锁同样依赖自定义同步器实现同步功能,而读写的状态就是同步器的同步状态。因为读写锁的特性,所以读写锁需要在同步状态上维护多个读线程和一个写线程的状态,所以该状态的设计成为了实现读写锁的关键。ReentrantReadWriteLock是将一个int变量按位切割分成两部分维读与写状态,高16位表示读,低16位表示写,如下图:

上图表示一个线程已经获取了写锁,并且重进入了一次,同时也连续获取了3次读锁。读写锁通过位运算能够迅速的写各自的状态,假设当前同步状态的值是S ,写状态等于S & 0x0000ffff把高16位全部抹去得到写锁的状态,读状态等于S >>>16(无符号补0右移16位),当写状态加1时,等于S+1;读状态加1 时,等于S+(1 << 16),也就是 S+ 0x00010000 .
ReentrantReadWriteLock读写锁实现分析的更多相关文章
- 锁对象-Lock: 同步问题更完美的处理方式 (ReentrantReadWriteLock读写锁的使用/源码分析)
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...
- Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析
目录 ReadWriteLock读写锁概述 读写锁案例 ReentrantReadWriteLock架构总览 Sync重要字段及内部类表示 写锁的获取 void lock() boolean writ ...
- ReentrantReadWriteLock读写锁的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- ReentrantReadWriteLock读写锁的使用2
本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 这一节我们做一个缓存系统. 在读本节前 请先阅读 ReentrantReadWriteLock读写锁的使用1 第一 ...
- ReentrantReadWriteLock读写锁简单原理案例证明
ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...
- java并发锁ReentrantReadWriteLock读写锁源码分析
1.ReentrantReadWriterLock 基础 所谓读写锁,是对访问资源共享锁和排斥锁,一般的重入性语义为如果对资源加了写锁,其他线程无法再获得写锁与读锁,但是持有写锁的线程,可以对资源加读 ...
- 多线程高并发编程(4) -- ReentrantReadWriteLock读写锁源码分析
背景: ReentrantReadWriteLock把锁进行了细化,分为了写锁和读锁,即独占锁和共享锁.独占锁即当前所有线程只有一个可以成功获取到锁对资源进行修改操作,共享锁是可以一起对资源信息进行查 ...
- java多线程:ReentrantReadWriteLock读写锁使用
Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁 ...
- AQS系列(三)- ReentrantReadWriteLock读写锁的加锁
前言 前两篇我们讲述了ReentrantLock的加锁释放锁过程,相对而言比较简单,本篇进入深水区,看看ReentrantReadWriteLock-读写锁的加锁过程是如何实现的,继续拜读老Lea凌厉 ...
随机推荐
- luogu P3808 【模板】AC自动机(简单版)
题目背景 这是一道简单的AC自动机模板题. 用于检测正确性以及算法常数. 为了防止卡OJ,在保证正确的基础上只有两组数据,请不要恶意提交. 管理员提示:本题数据内有重复的单词,且重复单词应该计算多次, ...
- [NSURL URLWithString:] 返回nil
具体问题原因是url中输入的有中文,那么这个就看作非法的字符无法识别.这种的必须使用post方式来发送消息.具体为: tmp = mainurl; [parameters app ...
- clipRect 介绍
clipRect 介绍 博客分类: android android的clip有以下两点疑问: Clip(剪切)的时机 Clip中的Op的参数的意思. 通常咱们理解的clip(剪切),是对已经存 ...
- iOS集成百度地图方法步骤
前言:app中的导航功能越来越流行,现在我自己做的项目中也有此需求,做过了后记录下笔记. 由于源代码保密所以这里仅仅提供demo,下面是效果图 一:iOS地图SDK 1.打开 百度地图api链接 i ...
- 解决Gradle执行命令时报Could not determine the dependencies of task ':compileReleaseJava'.
Could not determine the dependencies of task ':compileReleaseJava'. > failed to find target andro ...
- android RecycleView复杂多条目的布局
用RecycleView来实现布局形式.默认仅仅能指定一种布局格式.可是实际中我们的布局常常会用到多种类型的布局方式.怎样实现呢? 今天来说下经常使用的2钟方式. 第一种: 通过自己定义addHead ...
- MySQL高可用之——keepalived+互为主从
目标:配置一个keepalived双机热备架构,并配置主从复制 规划: master1 zlm177 192.168.17.177 master2 zlm188 192 ...
- 《图论》——广度优先遍历算法(BFS)
十大算法之广度优先遍历: 本文以实例形式讲述了基于Java的图的广度优先遍历算法实现方法,详细方法例如以下: 用邻接矩阵存储图方法: 1.确定图的顶点个数和边的个数 2.输入顶点信息存储在一维数组ve ...
- opencv yuv420与Mat互转
项目用到opencv 融合图片的功能,经过一天的调试,达到预期目标,先将如何调用opencv库实现YUV42与Mat互转记录下来. 一.下载opencv编译的库下载地址是:http://opencv. ...
- ORCAD元件属性白色区域和黄色区域的理解
白色部分为instance属性,黄色部分为occurence 属性 在平坦式电路中,黄色部分是默认不显示的. 在层次式电路中,黄色部分会显示. 如果这两个区域的Reference不同,以黄色 ...