使用读写锁可以大幅度提升性能,在读远大于写线程数目时,
本文主要是关于读写锁的一些疑惑 ,主要是实现缓存时,下面这段代码演示了常见的实现缓存的代码:(你可以在很多其他的博客中看到:)
我这里参考了: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 看到会立即回复.