重入
此锁允许 reader 和 writer 按照 ReentrantLock 的样式重新获取读取锁或写入锁。在写入线程保持的所有写入锁都已经释放后,才允许重入 reader 使用它们。
此外,writer 可以获取读取锁,但反过来则不成立。在其他应用程序中,当在调用或回调那些在读取锁状态下执行读取操作的方法期间保持写入锁时,重入很有用。如果 reader 试图获取写入锁,那么将永远不会获得成功。
锁降级
重入还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不可能的。这样会导致死锁。
如果锁由读线程持有,而另一个线程请求写入锁,那么其他读线程无法获取读取锁,直到写线程使用完,并释放了锁。写入所只能有唯一的所有者,并且只能由获取该锁的线程释放。当锁的持有时间较长并且大部分操作不会修改所有资源时,那么读写锁能提高并发性。
下面的代码展示了如何利用
重入来执行升级缓存后的锁降级(为简单起见,省略了异常处理):
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
分享到:
相关推荐
在上面的代码中,我们使用了RedisTemplate来操作Redis缓存,并使用ReentrantReadWriteLock来实现读写锁,以确保缓存的线程安全。 最后,我们需要在Mapper接口中使用自定义的缓存管理类: ```java @Mapper public ...
ReentrantReadWriteLock 是 ReadWriteLock 接口的一个实现类,它继承自 ReentrantLock,提供了读写锁的功能。在 ReentrantReadWriteLock 中,有两个内部类:ReadLock 和 WriteLock,这两个内部类分别实现了读锁和写...
在Java中,我们可以自己实现一个简单的缓存系统,以满足基本的缓存需求。以下是一个简化的实现过程。 首先,我们需要创建一个表示缓存对象的类。这个类应该包含存储的数据、数据的有效期以及最后更新的时间。如`...
5. **SerializedCache**:实现缓存数据的序列化和反序列化。 6. **SoftCache**:使用软引用管理缓存项。 7. **SynchronizedCache**:提供线程安全的缓存访问。 8. **WeakCache**:使用弱引用管理缓存项。 9. **...
ReentrantReadWriteLock是Java并发包中的一个核心类,它提供了读写锁的实现,使得多个线程可以并发读取共享资源,但写操作是互斥的,即同一时间只能有一个线程进行写操作。这种设计大大提高了多线程环境下对共享资源...
`ReentrantReadWriteLock`是Java并发库中提供的一个可重入的读写锁实现。它通过两个独立的锁——读锁和写锁来实现读写分离的目的。 - **读锁**:允许多个线程同时获取,因为读操作通常是非破坏性的。 - **写锁**:...
读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁ReentrantReadWriteLock&StampLock详解_e读写锁...
在Java多线程并发编程中,ReentrantReadWriteLock(可重入读写锁)是一个重要的同步工具,它属于Java并发包(java.util.concurrent.locks)中的一个类。这个锁提供了比标准的synchronized关键字更细粒度的控制,允许...
- **读写锁(ReadWriteLock)**:ReentrantReadWriteLock是Lock的一个实现,分为读锁(ReadLock)和写锁(WriteLock),允许多个读线程同时访问,但写锁是独占的。 - **条件变量(Condition)**:Lock接口提供了一...
Java的ReentrantReadWriteLock是Java并发包`java.util.concurrent.locks`中的一个重要工具,它提供了一种更细粒度的锁定机制,相比普通的独占锁(如ReentrantLock)在某些场景下能显著提高程序的并发性能。...
MyBatis是一个流行的Java持久层框架,它提供了一套高效的缓存机制来优化数据库操作的性能。这个机制包括一级缓存和二级缓存,旨在减少不必要的数据库查询,提高应用程序的响应速度。 一级缓存是基于PerpetualCache...
在“panda-demo”项目中,作者可能创建了两个版本的代码,一个使用synchronized,另一个使用ReadWriteLock。然后通过性能测试(如JMH),对比它们在处理大量并发读写操作时的性能差异。通常,如果读操作远多于写操作...
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
ReadWriteLock的使用,实际上由于ReadWriteLock是一个接口,所以实际使用的是ReentrantReadWriteLock子类。同时ReadWriteLock的使用其实也是比较简单的,就是读写的锁的使用以及注意事项而已。
6.5 深入理解 AQS之 ReentrantReadWritelock 实战副本.mp4
本篇文章将深入探讨一个简单的多线程LRUCache实现及其测试。 一、LRUCache原理 LRUCache的核心思想是:当缓存满时,最近最少使用的数据会被移除,为新数据腾出空间。这种策略基于“最近使用过的数据更可能在未来被...
6.5 深入理解 AQS之 ReentrantReadWritelock 实战副本副本.mp4
关于读写锁算法的Java实现及思考,是一个深入探讨了多线程环境下资源访问控制机制的主题。在现代软件开发中,尤其是并发编程领域,读写锁(ReadWriteLock)是一种非常重要的同步工具,它允许多个线程同时进行读操作...
ReentrantReadWriteLock 读写锁除了保证写操作对读操作可见性以及并发行提升外,简化了读写交互场景开发