关于正确使用读写锁的思考

13 篇文章 0 订阅
4 篇文章 0 订阅

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值