关于正确使用读写锁的思考
2015-09-23 21:19
411 查看
使用读写锁可以大幅度提升性能,在读远大于写线程数目时,
本文主要是关于读写锁的一些疑惑 ,主要是实现缓存时,下面这段代码演示了常见的实现缓存的代码:(你可以在很多其他的博客中看到:)
我这里参考了:http://blog.csdn.net/yangfanend/article/details/7381530 这篇博客中的实现 如下:
这里主要有2个疑问:
[1]line3 位置的判断是否是必须的?
[2]line3 位置的判断是否合理?
ps: 在line5的注释. //line5 – 这里是否还应将查到的数据放入缓存 不然无法实现cache效果.
下面说下我的见解,也希望有大牛可以一起指导下^-^
问题1:line3的位置是否是必须的?
答:是需要重新检查的. 考虑有线程1 执行到line1 后,然后被线程2抢先执行(切换到线程2去执行),那么很有可能这个key是已经被线程2put到缓存中去了. 所以是必须要重新检查的.
问题2:line3的位置的判断是否合理?
(1)这里我认为是不合理的.理由如下,Object value = null; 当获取未空的时候,这里获取的value对象是一个线程栈内的局部变量(意即它不会被别的线程更改,对它的访问是不存在多线程问题的,所以当它是空后,是不会变为非空的),那么我们在line3的检查是不合理的.
(2)如果需要检查,那么我们需要在获取写锁的情况下,重新调用map.get(key)来判断对象是否存在.(line4)
如下:
我看到了jdk api doc中的一个例子:ReentrantReadWriteLock
是不是很像?
但是代码的关键区别(line3)就是一个是用的:
为啥jdk的实现没有问题呢?
因为cacheValid是对象的一个状态变量 所以重新检查是有必要的.
而value不是(当获取不到时),它只是线程栈中的一个对象.不同的线程 该对象是不会一样的.
不知道这样的理解是否正确?
欢迎一起讨论,你可以发送邮箱到gaoxingliang@outlook.com 看到会立即回复.
本文主要是关于读写锁的一些疑惑 ,主要是实现缓存时,下面这段代码演示了常见的实现缓存的代码:(你可以在很多其他的博客中看到:)
我这里参考了:http://blog.csdn.net/yangfanend/article/details/7381530 这篇博客中的实现 如下:
public class ReadWriteLockCache { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub } private Map<String, Object> cache = new HashMap<String, Object>(); private ReadWriteLock rwl = new ReentrantReadWriteLock(); public Object getData(String key) { rwl.readLock().lock(); Object value = null; try { value = cache.get(key); if (value == null) { rwl.readLock().unlock();//line1 rwl.writeLock().lock();//line2 try { if (value == null)//line3 { value = "aaaa";// 实际失去queryDB(); //line5 -- 这里是否还应将查到的数据放入缓存 不然无法实现cache效果. cache.put(key, value); } } finally { rwl.writeLock().unlock(); } rwl.readLock().lock(); } } finally { rwl.readLock().unlock(); } return value; } }
这里主要有2个疑问:
[1]line3 位置的判断是否是必须的?
[2]line3 位置的判断是否合理?
ps: 在line5的注释. //line5 – 这里是否还应将查到的数据放入缓存 不然无法实现cache效果.
下面说下我的见解,也希望有大牛可以一起指导下^-^
问题1:line3的位置是否是必须的?
答:是需要重新检查的. 考虑有线程1 执行到line1 后,然后被线程2抢先执行(切换到线程2去执行),那么很有可能这个key是已经被线程2put到缓存中去了. 所以是必须要重新检查的.
问题2:line3的位置的判断是否合理?
(1)这里我认为是不合理的.理由如下,Object value = null; 当获取未空的时候,这里获取的value对象是一个线程栈内的局部变量(意即它不会被别的线程更改,对它的访问是不存在多线程问题的,所以当它是空后,是不会变为非空的),那么我们在line3的检查是不合理的.
(2)如果需要检查,那么我们需要在获取写锁的情况下,重新调用map.get(key)来判断对象是否存在.(line4)
如下:
if (value == null) { rwl.readLock().unlock();//line1 rwl.writeLock().lock();//line2 try { value = cache.get(key);//line4 if (value == null)//line3 { value = "aaaa";// 实际失去queryDB(); //then put in cache map cache.put(key, value); } } finally { rwl.writeLock().unlock(); } rwl.readLock().lock(); }
我看到了jdk api doc中的一个例子:ReentrantReadWriteLock
package concurrent; import java.util.concurrent.locks.ReentrantReadWriteLock; 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();//line1 rwl.writeLock().lock();//line2 try { // Recheck state because another thread might have // acquired write lock and changed state before we did. if (!cacheValid)//line3 { data = "aaa"; 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(); } } }
是不是很像?
但是代码的关键区别(line3)就是一个是用的:
if (value == null)//line3 if (!cacheValid)//line3
为啥jdk的实现没有问题呢?
因为cacheValid是对象的一个状态变量 所以重新检查是有必要的.
而value不是(当获取不到时),它只是线程栈中的一个对象.不同的线程 该对象是不会一样的.
不知道这样的理解是否正确?
欢迎一起讨论,你可以发送邮箱到gaoxingliang@outlook.com 看到会立即回复.
相关文章推荐
- java06
- c++primer第三章习题(2)
- 利用sourceinsight宏(Quicker.em)提高编码效率和质量
- 【codevs2602】最优分解方案 dp+高精
- 被注入病毒的苹果 还能得到用户宠爱吗?
- FragmentPagerAdapter默认加载2项,返回时为创建视图
- 集线器、交换机、路由器、网关
- java05
- 活着
- shell数组
- 中国联通3g上网设置
- hadoop学习之hadoop集群功能简单测试验证
- 十、行为型模式之观察者、命令、状态-----《大话设计模式》
- 2015年秋季个人阅读计划(修改版)
- 学习笔记:利用awk分析大型WEB日志 //待完善
- 关于IP地址分类自己的一些理解
- 无锁编程(七) - 实战
- ios实现点击空白处关闭键盘
- Objective-C type encodings
- 《计算机操作系统》总结三(内存管理)