gpt4 book ai didi

Java 读写锁节省内存资源

转载 作者:行者123 更新时间:2023-11-30 02:03:07 25 4
gpt4 key购买 nike

内存中存在大量 R 类型的对象。修改对象需要写锁,读取对象需要读锁。我可以将 ReadWriteLock 存储为类 R 的私有(private)成员,但是,我想节省内存。在任何时候,只有一小部分对象被修改或读取。有多种方法可以决定不存储特定资源的读写锁(例如,如果在一段时间内没有读取或写入该资源,t)。出于此问题的目的,假设可以定期确定可以删除资源的锁。但是,请记住,当在线程中删除资源的锁时,一个或多个其他线程可能会尝试修改或读取该资源。所有这一切都发生在多线程环境中。您将如何以最少的锁定来实现这一点?

例如,实现此目的的一种方法是将读写锁存储在并发映射中:

Map<R,ReadWriteLock> map = new ConcurrentHashMap<>();

当确定可以删除资源的读写锁时,然后将其从映射中删除。然而,正如上面提到的,在决定删除该条目之后和该条目被删除之前,其他线程可能想要获取读锁或写锁。

您可能认为可以使用computeifabsentremove的组合。然而,这是行不通的。例如:

//--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 版本。

总而言之,问题是如何维护资源集合的读写锁,而不必为集合中的每个对象存储读写锁并最大限度地减少锁争用。

最佳答案

您可以使用 computecomputeIfPresent 方法来发挥自己的优势。重要的是在消费者内部添加/锁定/删除,以原子方式完成。

注意:您在示例中使用了 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);
}
}
}

注意我如何使用

  • lock计算以创建新锁并立即锁定它们
  • unlock 中的computeIfPresent 检查是否有锁
  • computeIfPresentcleanUp 中检查是否需要锁,而在检查读锁计数时没有另一个线程锁定写锁

现在,unlock 相当无用(除了空检查,这只是一种预防措施)。在 unlock 中返回 null 可以很好地清理不必要的锁,并使 cleanUp 过时,但可能会增加创建新锁的需求。这取决于锁的使用频率。

当然,您可以添加方便的读/写方法,而不必提供 getter whichLock

关于Java 读写锁节省内存资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52169601/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com