ReentrantReadWriteLock读写锁的使用2
本文可作为传智播客《张孝祥-Java多线程与并发库高级应用》的学习笔记。
这一节我们做一个缓存系统。
在读本节前
请先阅读
ReentrantReadWriteLock读写锁的使用1
第一版
public class CacheDemo {
private Map<String, Object> cache = new HashMap<String, Object>();
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
System.out.println("ss "+cd.getData2("ss"));
System.out.println("ss "+cd.getData2("ss"));
System.out.println("mm "+cd.getData2("mm"));
System.out.println("mm "+cd.getData2("mm"));
}
public Object getData2(String key){
Object o=cache.get(key);
if (o==null) { //标识1
System.out.println("第一次查 没有"+key);
o=Math.random(); //实际上从数据库中获得
cache.put(key, o);
}
return o;
}
}
运行结果:
第一次查 没有ss
ss 0.4045014284225158
ss 0.4045014284225158
第一次查 没有mm
mm 0.9994663041529088
mm 0.9994663041529088
似乎没有问题。
你觉得呢?
如果有三个线程同时第一次到了getData2()的标识1处(所查找的key也都一样),一检查o为null,然后就去查数据库。在这种情况下,就等于三个线程查同一个key,然后都去了数据库。
这显然是不合理的。
第二版 synchronized
最简单的办法
public synchronized Object getData2(String key){
//.....
}
第三版 锁
上一节我们提到了ReentrantLock可以替换synchronized,前者是一种更为面向对象的设计。那我们试试。
private Lock l=new ReentrantLock(); //l作为CacheDemo的成员变量
public Object getData3(String key) {
l.lock();
Object o=null;
try {
o = cache.get(key);
if (o == null) { // 标识1
System.out.println("第一次查 没有" + key);
o = Math.random(); // 实际上从数据库中获得
cache.put(key, o);
}
} finally {
l.unlock();
}
return o;
}
第三版可以吗?
可以个p。
为什么,自己想。
我们得使用ReentrantReadWriteLock,在同一个方法中既有读锁也有写锁。
首先我又一个问题:
对同一个线程,可以在加了读锁后,没有解开读锁前,再加写锁吗?
换句话说,下面的代码会停吗?
public class CacheDemo {
private Map<String, Object> cache = new HashMap<String, Object>();
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
cd.getData2();
}
public void getData2(){
rwl.readLock().lock();
System.out.println(1);
rwl.writeLock().lock();
System.out.println(2);
rwl.readLock().unlock();
System.out.println(3);
rwl.writeLock().unlock();
System.out.println(4);
}
}
亲自试一下,控制台输出1后,程序就不动了。
说明加写锁前,读锁得先解开。
第四版
即有读锁,又有写锁
public static void main(String[] args) {
CacheDemo cd = new CacheDemo();
System.out.println("ss "+cd.getData4("ss"));
System.out.println("ss "+cd.getData4("ss"));
System.out.println("mm "+cd.getData4("mm"));
System.out.println("mm "+cd.getData4("mm"));
}
public Object getData4(String key){
rwl.readLock().lock();
Object o=null;
try {
o=cache.get(key);
if (o==null) {
System.out.println("第一次查 没有"+key);
rwl.readLock().unlock(); //标识4
rwl.writeLock().lock();
try {
if(value==null){ //标识0
value = "aaaa";//实际调用 queryDB();
cache.put(key,value);
}
} finally {
rwl.writeLock().unlock();
}
rwl.readLock().lock(); //标识1
}
}finally {
rwl.readLock().unlock(); //标注2
}
return o;
}
结果
第一次查 没有ss
ss 0.5989899889645358
ss 0.5989899889645358
第一次查 没有mm
mm 0.8534424949014686
mm 0.8534424949014686
这个还有三个问题
1为什么在标识2处还得解锁。
这个答案在上一节已经提过。
2为什么在标识1出还得加锁?
如果标识1处不加锁,且程序一直正常执行,那么到标识2处,它解谁的锁?
3为什么标识0出还要检查一次?
如果同时又三个线程运行到标识4(查找相同的key),其中一个获得写锁,写入数据后,释放了写锁。此时另外两个线程还需要去数据库跑一趟么?
另外把标识1处的加读锁,放到finally的前面称之为降级锁。对降级锁,我目前也不太清楚。
官方文档中给了一个例子就是关于降级锁,如下
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}
感谢glt
ReentrantReadWriteLock读写锁的使用2的更多相关文章
- ReentrantReadWriteLock读写锁的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- 锁对象-Lock: 同步问题更完美的处理方式 (ReentrantReadWriteLock读写锁的使用/源码分析)
Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...
- Java并发包源码学习系列:ReentrantReadWriteLock读写锁解析
目录 ReadWriteLock读写锁概述 读写锁案例 ReentrantReadWriteLock架构总览 Sync重要字段及内部类表示 写锁的获取 void lock() boolean writ ...
- ReentrantReadWriteLock读写锁简单原理案例证明
ReentrantReadWriteLock存在原因? 我们知道List的实现类ArrayList,LinkedList都是非线程安全的,Vector类通过用synchronized修饰方法保证了Li ...
- ReentrantReadWriteLock读写锁详解
一.读写锁简介 现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁.在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源:但是如果一个线 ...
- java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板
写锁降级为读锁,但读锁不可升级或降级为写锁. 锁降级是为了让当前线程感知到数据的变化. //读写锁 private ReentrantReadWriteLock lock=new ReentrantR ...
- java中ReentrantReadWriteLock读写锁的使用
Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象. 读写锁:分为读 ...
- java多线程:ReentrantReadWriteLock读写锁使用
Lock比传统的线程模型synchronized更多的面向对象的方式.锁和生活似,应该是一个对象.两个线程运行的代码片段要实现同步相互排斥的效果.它们必须用同一个Lock对象. 读写锁:分为读锁和写锁 ...
- ReentrantReadWriteLock读写锁的使用1
本文可作为传智播客<张孝祥-Java多线程与并发库高级应用>的学习笔记. 一个简单的例子 两个线程,一个不断打印a,一个不断打印b public class LockTest { publ ...
随机推荐
- studio中碰到的jni问题:java.lang.UnsatisfiedLinkError
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52606328 studio中碰到 ...
- 在Gazebo中使用DEM構建起伏地形環境
所需資料下載地址: 1. https://bitbucket.org/osrf/gazebo_tutorials/raw/default/dem/files/ 数字高程模型(致謝谷歌翻譯)概述数字高程 ...
- Python中使用rrdtool结合Django进行带宽监控
我们有个网关需要做下带宽监控,能获取这个数据的唯一方法就是登录到管理界面查看.然后咱就写了个模拟登录的爬虫,定时抓取数据用rrdtool存储,最后通过Django来展示.这里就涉及了python的rr ...
- 剑指Offer——Trie树(字典树)
剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种的单词.对于每一个单词,我们要判断他出没出现过,如果出现了,求第一次出现在第几个位 ...
- hive的strict模式;where,group by,having,order by同时使用的执行顺序
主要限制三种情况 (1) 有partition的表查询需要加上where子句,筛选部分数据实现分区裁剪,即不允许全表全分区扫描,防止数据过大 (2) order by 执行时只产生一个reduce,必 ...
- 【Unity Shaders】ShadowGun系列之一——飞机坠毁的浓烟效果
写在前面 最近一直在思考下面的学习该怎么进行,当然自己有在一边做项目一边学OpenGL,偶尔翻翻论文之类的.但是,写shader是一个需要实战和动手经验的过程,而模仿是前期学习的必经之路.很多人都会问 ...
- 【java集合框架源码剖析系列】java源码剖析之TreeMap
注:博主java集合框架源码剖析系列的源码全部基于JDK1.8.0版本.本博客将从源码角度带领大家学习关于TreeMap的知识. 一TreeMap的定义: public class TreeMap&l ...
- 文章标题 Oracle数据库中dual表使用
一. 业务场景 业务流程需要进行写入和更新的比较,所以有原表和历史表. 要求表中的ID唯一性,以及两张表的ID关联,另外后续可能数据库会进行迁移 二.方案选择 方案一:id设置为int型自增长. 这种 ...
- 利用并查集+贪心解决 Hdu1232
畅通工程 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submi ...
- Hessian源码分析--HessianProxyFactory
HessianProxyFactory是HessianProxy的工厂类,其通过HessianProxy来生成代理类. 如下面代码: HessianProxyFactory factory = new ...