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凌厉 ...
随机推荐
- java单例模式教程之java实现单例模式的8大方法
单例模式是Java中常用的设计模式之一.单例模式属于创建型模式,它提供了一种创建对象的最佳方式. 单例模式只创建类的一个对象,之后在一定范围为可任意调用,确保只有单个对象被创建.这个类提供了一种访问其 ...
- HDU4372 Buildings
@(HDU)[Stirling數, 排列組合] Problem Description There are N buildings standing in a straight line in the ...
- Java中没有C#的out关键字,但可以通过数组实现类似的效果
其实传递的就是数组的指针,里面的每一项的值还是那块内存,所以能直接操作里面的值.如果单纯传指定的值,那么里面操作的就是新的一块内存块. 用数组实现的效果如下: class B{ String cnt= ...
- git 怎样删除远程仓库的最近一次错误提交?
假设你有3个commit如下: commit 3 commit 2 commit 1 其中最后一次提交commit 3是错误的,那么可以执行: git reset --hard HEAD~1 你会发现 ...
- Majority Number
题目描写叙述 链接地址 解法 算法解释 题目描写叙述 Given an array of integers, the majority number is the number that occurs ...
- selector在手机上或浏览器显示各种姿势(虚拟下拉菜单)
测试机型:小米.华为.苹果 测试浏览器:Chrome.Safari.Firefox 最后的结果就是你搞你的,我搞我的! 我认为这样漂亮,你认为那样漂亮(我认为你们都统一!) 因为项目时间紧,所以直接用 ...
- 【iOS】UIWebView的HTML5扩展之canvas篇
先前公布大那个所谓的"HTML5"扩展严格说来还算不是"HTML5".曲曲几行JS代码就自诩为HTML5扩展多少有些标题党的嫌疑. 而相比之下,本篇的主题can ...
- 2.nginx整合PHP
/usr/local/src/下都有什么:.tar.gz文件和解压之后的源码 /usr/local/下都有什么:源码编译之后的东西 安装mysql客户端 安装mysql服务端 启动mysql服务端 s ...
- 怎样在C语言里实现“面向对象编程”
有人觉得面向对象是C++/Java这样的高级语言的专利,实际不是这样.面向对象作为一种设计方法.是不限制语言的.仅仅能说,用C++/Java这样的语法来实现面向对象会更easy.更自然一些. 在本节中 ...
- Cocos2d-X中的粒子
Cocos2d-x引擎提供了强大的type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/particle-s ...