java - Java 对资源节约内存的读写锁定
问题描述
内存中有大量R类型的对象。修改对象需要写锁,读需要读锁。我可以将 ReadWriteLock 存储为类R的私有成员,但是,我想节省内存。在任何时候,只有一小部分对象被修改或读取。有多种方法可以决定不为特定资源存储读写锁(例如,如果它在一段时间内没有被读取或写入,t)。出于此问题的目的,假设可以定期确定可以删除资源的锁。但是,请记住,在线程中删除资源的锁时,一个或多个其他线程可能会尝试修改或读取该资源。所有这些都发生在多线程环境中。您将如何以最少的锁定量来实现这一点?
例如,一种方法是将读写锁存储在并发映射中:
Map<R,ReadWriteLock> map = new ConcurrentHashMap<>();
当确定可以删除资源的读写锁时,将其从映射中删除。然而,如上所述,在决定删除条目之后,在删除条目之前,其他线程可能想要获取读或写锁。
您可能认为可以使用computeifabsent和remove的组合。但是,这是行不通的。例如:
//--Thread1 write lock--
ReadWriteLock rwl = map.computeIfAbsent(r, r -> new ReadWriteLock()); // 1
rwl.writeLock.lock(); // 4
//Modify r here
//--Thread2: Removing entry--
map.remove(r); // 2
//Thread3: write lock
ReadWriteLock rwl = map.computeIfAbsent(r, r-> new ReadWriteLock()); // 3
rwl.writeLock.lock(); // 5
//Modify r here.
问题是线程 1 的锁对象与线程 3 获得的锁不同,并且错误地允许同时发生两次写入。右边的数字表示执行顺序。
答案不需要使用上面示例中给出的并发映射,但它似乎是一个好的开始并提供对锁的并发访问。如果您确实使用并发映射,请随意将 ReadWriteLock 包装在另一个结构中或创建您自己的 ReadWriteLock 版本。
总之,问题是如何维护资源集合的读写锁,而不必为集合中的每个对象存储读写锁并最小化锁争用。
解决方案
您可以使用这些方法compute
并computeIfPresent
发挥自己的优势。重要的是在消费者内部进行添加/锁定/删除以自动完成。
注意:您putIfAbsent
在示例中使用了,但返回的是先前分配的值,而不是新分配的值。
public static class Locks<R>
{
private ConcurrentHashMap<R, ReentrantReadWriteLock> locks = new ConcurrentHashMap<>();
public void lock(R r, Function<ReentrantReadWriteLock, Lock> whichLock)
{
locks.compute(r, (key, lock) -> {
ReentrantReadWriteLock actualLock = lock == null ? new ReentrantReadWriteLock() : lock;
whichLock.apply(actualLock).lock();
return actualLock;
});
}
public void unlock(R r, Function<ReentrantReadWriteLock, Lock> whichLock)
{
locks.computeIfPresent(r, (key, lock) -> {
whichLock.apply(lock).unlock();
return lock; // you could return null here if lock is unlocked (see cleanUp) to remove it immediately
});
}
public void cleanUp()
{
for (R r : new ArrayList<>(locks.keySet()))
{
locks.computeIfPresent(r, (key, lock) -> locks.get(r).isWriteLocked()
|| locks.get(r).getReadLockCount() != 0 ? lock : null);
}
}
}
注意我如何使用
compute
inlock
创建新锁并立即锁定它们computeIfPresent
unlock
检查是否有锁computeIfPresent
在cleanUp
我检查读锁计数时检查是否需要锁而不需要另一个线程锁定写锁
现在,unlock
相当没用(除了空检查,这只是一种预防措施)。返回null
会unlock
很好地清理不必要的锁并使其cleanUp
过时,但可能会增加创建新锁的需求。这取决于使用锁的频率。
当然,您可以添加方便的读/写方法,而不必提供 getter whichLock
。
推荐阅读
- sharepoint - Sharepoint 自定义工作流程
- gitlab - 无法在集群上执行“helm install”。Tiller 由 gitab 安装
- c++ - C++ 只运行一段时间
- node.js - 安全访问渲染器进程中基于节点的模块
- angular - 拒绝在框架中显示“https://stackblitz.com/edit/zknwqv.run”,因为它将“X-Frame-Options”设置为“sameorigin”
- java - 如何在卡片布局下的面板中添加功能性 Java 键侦听器?
- python - 如何修复 ImportError:在 INSTALLED_APPS 中作为“apps.comments”存在时,没有名为 comments.models 的模块?
- pip - pip search 显示 apache-beam 2.9 但 pip install apache-beam 只安装了 apache-beam2.2
- c++ - C++ Vector Initialize with INT_MIN crash
- regex - sed:在块之后追加