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凌厉 ...
随机推荐
- Six ways to think like a journalist!
Journalists have the ability to state a thing more clearly. What can we learn from them to help us r ...
- 在c++11中你最惊讶的新feature是什么?
对我来说,我最惊讶竟然把对于多线程的支持加到标准中了.真的想不明确,对于c++这样一种语言.怎么会加进这个东西. 1. 由于各个平台的不同,对于多线程的支持会有很多平台独有的特色.这样c++标准的定义 ...
- scheme语言编写执行
scheme是lisp的一种 编辑器能够用emacs.网上有非常多教导怎样编写的 (begin (display "hello") (newline)) 编写完以.scm保存,这里 ...
- Android pull to Refresh 导入出错?
今天在导入 PuultoResfresh 的时候老是出错. error: [2014-09-28 10:04:44 - library] Unable to resolve target 'andro ...
- Num 34 : HDOJ : 1205 吃糖果 [ 狄利克雷抽屉原理 ]
抽屉原理: 桌上有十个苹果,要把这十个苹果放到九个抽屉里,不管如何放,我们会发现至少会有一个抽屉里面至少放两个苹果. 这一现象就是我们所说的" ...
- Ubuntu 登陆异常-输入正确的密码后还会返回到登陆界面的问题
问题表现: 启动到了登陆界面,输入对应的密码,发现一闪黑屏有返回到登陆界面,如此往复. 解决方法: 开机后在登陆界面按下shift + ctrl + F1进入tty命令行终端登陆,可以查看用户主目录下 ...
- CUDA vs GPGPU
http://www.kuqin.com/hardware/20080830/15726.html 一 在硬件和软件之间选择 “计算机软件和硬件的逻辑等价性”是计算机组成原理中一个非常重要的理 ...
- HDU1009_FatMouse' Trade【贪心】【水题】
FatMouse' Trade Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- Android 短信验证码控件
Android 短信验证码控件,便于项目中使用统一样式,统一提示改动.个人觉得挺好用的 <span style="font-size:18px;">public cla ...
- 基于EfCore的一个多租户Demo
六月份如愿的转入了架构组,先上手搞了个CI服务器,把架构组的几个项目撸到上面去了.效果不错,接着就把其他两个项目组有单元测试的项目撸上去了,在桌子上放了个显示器当大屏用. 因为公司准备进行一个大的系统 ...