Java 锁写入HashMap
我有一个正在异步更新的HashMap。我需要执行涉及映射的操作,这些操作要求映射在任务期间不改变状态,例如根据映射的内容对值进行排序Java 锁写入HashMap,java,multithreading,concurrency,Java,Multithreading,Concurrency,我有一个正在异步更新的HashMap。我需要执行涉及映射的操作,这些操作要求映射在任务期间不改变状态,例如根据映射的内容对值进行排序 有没有一种方法可以锁定一个映射,这样只会发生读操作,这会阻止所有的写线程,这样在映射被解锁之后,所有的修改都会发生?当我使用ConcurrentHashMap时,锁需要能够允许多个线程同时写入,我想利用ConcurrentHashMap的优点。ConcurrentHashMap不提供方法调用之间的线程安全,只在单个调用中提供。因此,如果希望在执行操作时保持映射不变
有没有一种方法可以锁定一个映射,这样只会发生读操作,这会阻止所有的写线程,这样在映射被解锁之后,所有的修改都会发生?当我使用ConcurrentHashMap时,锁需要能够允许多个线程同时写入,我想利用ConcurrentHashMap的优点。ConcurrentHashMap不提供方法调用之间的线程安全,只在单个调用中提供。因此,如果希望在执行操作时保持映射不变,则需要使用自己的锁包装对映射的调用 例如,使用ReadWriteLock:
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
当您更新地图时
readWriteLock.writeLock().lock();
// do the updates
readWriteLock.writeLock().unlock();
当你想看地图的时候
readWriteLock.readLock().lock();
// perform the read and its operations that rely on the map not changing
readWriteLock.readLock().unlock();
为什么不使用受可重入写回保护的
ConcurrentHashMap
,而是反向使用
myLock.writeLock().lock();
try {
return myMap.get(...);
} finally {
myLock.writeLock().unlock();
}
及
这非常难看,需要在注释中进行说明,以警告未经培训的后续开发人员,您在按住readLock
时实际上正在编写,在按住writeLock
时正在阅读,但我不明白为什么它不起作用
为了清晰起见,您甚至可以编写自己的inversedreentrandretrawritelock
,它封装了真实的reentrandretrawritelock
,以反转调用并使代码更具可读性
如@GPI所建议的,如果您希望处于读模式或写模式,但在该模式下保持并发性,则可以使用
AtomicInteger
或AtomicLong
实现锁定。正值表示处于读取模式,负值表示处于写入模式。处于零表示锁可以进入任一模式。这会立即变得更加复杂,因为您必须自己实现锁,而上面概述的具有独占读取的并发写入解决方案仅依赖于现有的API类。(如果您愿意,我肯定可以找到一个答案,但我无法立即提供答案)将HashMap包装到一个类中,在该类中放置某种类型的
boolean isLocked
假设您想从一个线程开始读取进程,您可以检查这个锁
if(!myClass.getLockState())
{ //perform whatever you want to do }
如果您确保所有线程都使用相同的
myClass
对象,这可能是锁定/解锁hashMap的简单解决方案。为什么不尝试使用ConcurrentHashMap?@TugrulAsik:ConcurrentHashMap没有选项锁定整个对象,使用单独的锁会破坏使用ConcurrentHashMap的好处。您还可以在常规HashMap周围包装一个锁。感谢您对ConcurrentHashMap的解释。在ConcurrentHashMap周围包装一个ReadWriteLock,让写入程序使用读锁,长时间运行的读操作使用写锁,这可能会带来性能优势,如果一次只执行一个长时间运行的读取操作,那么能够容忍地图在其下更改的读者两者都不会使用。不过,我担心双重同步开销。也许可以计时,看看它与受常规锁保护的HashMap相比如何。@user2248702如果有解决您问题的答案,您应该接受:)据我所知,ReadWriteLock一次只允许一个写入线程,这是正确的吗?如果是,我可以用什么来编写多线程?@user2248702-您可以使用ConcurrentHashMap
来实现这一点,但有一个额外的缺点-所有的写操作都不会在后续的读取中看到。@user2248702您的理解是正确的,ReadWriteLock是单写多读。多个作者将很难实现。你可以按照老兄的建议,或者在地图上画条。也就是说,有一个外部映射,它包装了n个其他映射,其中每个代理映射都有自己的读/写锁。然后可以通过键的hashcode选择代理映射。但首先,我建议保持简单,并在增加复杂性之前演示单个编写器解决方案的瓶颈?没有任何地方表明,当你锁定writeLock时,你一定是在写作。我知道这很难看,但你可以用readLock()写,用writeLock()读。@Rich lol。你说得对,这很难看。但它将实现OP的要求。我质疑多个作者和单个读者的语义。。听起来这里有一个更大的设计问题。也许更详细地说明为什么会出现这种情况会有所帮助。我不确定。。。虽然很难看,但它似乎符合我的要求。这将导致读取是单线程的。我认为OP考虑了(多线程读取)或(多线程写入),带有“或”互斥性。注释代码应该始终是最后的手段——只有当您正在做一些非常邪恶的事情,没有其他方法来解释它时才使用:在添加注释之前,我会尝试用类名和方法名将ReadWriteLock包装到另一个类中,这些类名和方法名可以解释发生了什么。好的,因此线程a调用geetLockState(),发现它是错误的,并开始做任何事情。如果此时线程B锁定锁并更改映射的内容,会发生什么情况?@james large hmmmm你是对的……愚蠢的我:)但是如果你可以将所有修改的“请求”放在concurrentLinkedQueue
中,并且一个线程实际读取该队列并对映射执行更改,会发生什么情况?所有其他线程都是只读访问。
if(!myClass.getLockState())
{ //perform whatever you want to do }