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凌厉 ...
随机推荐
- codevs贪吃的九头龙
传说中的九头龙是一种特别贪吃的动物.虽然名字叫“九头龙”,但这只是说它出生的时候有九个头,而在成长的过程中,它有时会长出很多的新头,头的总数会远大于九,当然也会有旧头因衰老而自己脱落.有一天,有M 个 ...
- laravel svn从win上传linux需要注意事项
一首页设置目录权限: /storage /bootstrap/cache 设置可写权限 二执行命令: php artisan key:generate
- TensorFlow 之 高层封装slim,tflearn,keras
tensorflow资源整合 使用原生态TensorFlow API来实现各种不同的神经网络结构.虽然原生态的TensorFlow API可以很灵活的支持不同的神经网络结构,但是其代码相对比较冗长,写 ...
- c# Dictionary拓展2个key得到1个value
using System.Collections.Generic; using System.Collections; Dictionary<Tuple<int, int>, int ...
- 直接返回list不封装的结果集
直接返回list不封装的结果集,在Jsp访问方式: 1.封装成map访问 2.用jstl: <c:forEach var="images" items="${lis ...
- golang map to struct
http://stackoverflow.com/questions/26744873/converting-map-to-struct func SetField(obj interface{}, ...
- Android相关工具下载(ADT、NDK等等)
一个非常牛掰的网站,可以下载很多Android相关的工具等 网址为: http://www.androiddevtools.cn/
- shell脚本编写-自动部署及监控
1.编写脚本自动部署反向代理.web.nfs: I.部署nginx反向代理两个web服务,调度算法使用加权轮询 II.所有web服务使用共享存储nfs,保证所有web都对其有读写权限,保证数据一致性: ...
- Android摄像头採集的视频数据流怎样通过Socket实时发送到目标服务端
分两块: 1.取得摄像头採集的视频流 2.发送到server端 protected MediaRecorder mMediaRecorder; private LocalServerSocket mL ...
- nginx使用指南
1.执行nginx 能够执行nginx命令开启nginx: nginx 假设nginx已经开启了,能够执行nginx命令加-s 參数来控制nginx的执行 nginx -s signal signal ...