使用ReentrantReadWriteLock实现一个简单的缓存Demo

[b]重入[/b]
此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入 reader 使用它们。
此外,writer 可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。如果 reader 试图获取写入锁,那么将永远不会获得成功。
[b]锁降级[/b]
重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。这样会导致死锁。
如果锁由读线程持有,而另一个线程请求写入锁,那么其他读线程无法获取读取锁,直到写线程使用完,并释放了锁。写入所只能有唯一的所有者,并且只能由获取该锁的线程释放。当锁的持有时间较长并且大部分操作不会修改所有资源时,那么读写锁能提高并发性。

下面的代码展示了如何利用[b]重入[/b]来执行升级缓存后的锁降级(为简单起见,省略了异常处理):

class CachedData {
Object data;
volatile boolean cacheValid;
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();
// 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();
rwl.writeLock().unlock(); // Unlock write, still hold read
}

use(data);
rwl.readLock().unlock();
}
}

根据这个实例启示。我们可以写一个简单的CacheDemo,它可以在查询一个HashMap时实现同步,并且在缓存中查询不到数据时,从外部写入数据。示例如下:
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class CacheDemo
{
private final Map<String, Object> cache = new HashMap<String, Object>();
private final ReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
private final Random random = new Random();

public Object getData(String key)
{
r.lock();
Object value = null;
try
{
value = cache.get(key);
if (value == null)
{
// Must release read lock before acquiring write lock
r.unlock();
w.lock();
r.lock(); // Downgrade by acquiring read lock before releasing write lock
try
{
// Recheck state because another thread might have acquired
// write lock and changed state before we did.
value = cache.get(key);
if (value == null)
{
slowly();
value = random.nextInt(1000);// 实际是去query DB 或者从其他地方获取
System.out.println(Thread.currentThread().getName() + " produces value "
+ value + ", for key " + key);
cache.put(key, value);
}
}
finally
{
w.unlock();// Unlock write, still hold read
}
}
}
finally
{
r.unlock();
}
System.out.println(Thread.currentThread().getName() + " gets value " + value);
return value;
}

private void slowly()
{
try
{
Thread.sleep(1);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}

public static void main(String[] args)
{
ExecutorService executor = Executors.newCachedThreadPool();
final CacheDemo cache = new CacheDemo();
final Random random = new Random();

for (int i = 0; i < 30; i++)
{
executor.execute(new Runnable()
{
@Override
public void run()
{
for (int j = 0; j < 50; j++)
{
String key = "" + random.nextInt(2);
cache.getData(key.trim());
}
}
});
}
executor.shutdown();
}
}

得到结果:
pool-1-thread-1 produces value 675, for key 0
pool-1-thread-1 gets value 675
pool-1-thread-1 produces value 956, for key 1
pool-1-thread-1 gets value 956
pool-1-thread-1 gets value 675
pool-1-thread-2 gets value 956
pool-1-thread-4 gets value 675
pool-1-thread-4 gets value 956
pool-1-thread-8 gets value 956
pool-1-thread-8 gets value 956
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值