Java 我的ConcurrentHashmap';s的值类型是List,如何使附加到该列表的线程安全?
我的类扩展自ConcurrentHashmap[String,immutable.List[String]] 它有两种方法:Java 我的ConcurrentHashmap';s的值类型是List,如何使附加到该列表的线程安全?,java,scala,concurrency,thread-safety,Java,Scala,Concurrency,Thread Safety,我的类扩展自ConcurrentHashmap[String,immutable.List[String]] 它有两种方法: def addEntry(key: String, newList: immutable.List[String]) = { ... //if key exist,appending the newList to the exist one //otherwise set the newList as the value } def
def addEntry(key: String, newList: immutable.List[String]) = {
...
//if key exist,appending the newList to the exist one
//otherwise set the newList as the value
}
def resetEntry(key: String): Unit = {
this.remove(key)
}
为了使addEntry方法线程安全,我尝试了:
this.get(key).synchronized{
//append or set here
}
但如果键不存在,则会引发空指针异常,并在同步之前使用putIfAbsent(键,new immutable.List())将不起作用,因为在putIfAbsent之后和进入同步块之前,该键可能会被resetEntry删除
使addEntry和resetEntry这两种同步方法都能工作,但锁太大
那么,我能做什么呢
另外,这篇文章和whileplz很相似,请帮我找出除了一般指南之外的其他编码方法
--更新--
签出,在将近3年多后解决了这个问题。您能简单地清除条目而不是删除条目吗?您仍然可以使用同步列表并确保原子性
def resetEntry(key: String, currentBatchSize: Int): Unit = {
this.get(key).clear();
}
这样做的前提是每个键都有一个条目。例如,如果
this.get(key)=null
,则需要插入一个新的sychronizedList
,该列表也应充当clear。您可以尝试一种受CAS(比较和交换)过程启发的方法:
(在伪java scala代码中,因为我的scala仍处于初级阶段)
三年多以后,我想现在我可以回答我的问题了 最初的问题是: 我得到了一个
ConcurrentHashMap[String,List]
,许多线程都在向它添加值,如何使它成为线程安全的
使addEntry()
同步将起作用,对吗
synchronize(map.get(key)){
map.append(key, value)
}
在大多数情况下是,除非map.get(key)
为null,这将导致NullPointerException
那么如何添加map.putIfAbsent(键,新列表)
如下:
map.putIfAbsent(key, new List)
synchronize(map.get(key)){
map.append(key, value)
}
class MyMap extends ConcurrentHashMap{
val lock = new ReentrantReadWriteLock();
def addEntry(key: String, newList: immutable.List[String]) = {
lock.readLock.lock()
//if key exist,appending the newList to the exist one
//otherwise set the newList as the value
this.putIfAbsent(key, new List())
this(key).synchronized{
this(key) += newList
}
lock.readLock.unlock()
}
def resetEntry(key: String, currentBatchSize: Int): Unit = {
lock.writeLock.lock()
this.remove(key)
lock.writeLock.unlock()
}
}
现在更好了,但是如果在putIfAbsent()
另一个名为resetEntry()
的线程之后,我们将再次看到NullPointerException
使addEntry和resetEntry都同步的方法可以工作,但锁太大
那么,追加时的MapEntry级别锁和重置时的Map级别锁呢
这是可重入的TreadWriteLock:
当调用addEntry()
时,我们获取一个映射的共享锁,这使附加尽可能并发;当调用resetEntry()
时,我们获取一个独占锁,以确保没有其他线程同时更改映射
代码如下所示:
map.putIfAbsent(key, new List)
synchronize(map.get(key)){
map.append(key, value)
}
class MyMap extends ConcurrentHashMap{
val lock = new ReentrantReadWriteLock();
def addEntry(key: String, newList: immutable.List[String]) = {
lock.readLock.lock()
//if key exist,appending the newList to the exist one
//otherwise set the newList as the value
this.putIfAbsent(key, new List())
this(key).synchronized{
this(key) += newList
}
lock.readLock.unlock()
}
def resetEntry(key: String, currentBatchSize: Int): Unit = {
lock.writeLock.lock()
this.remove(key)
lock.writeLock.unlock()
}
}
但是我有成千上万个键,而且这个数字会不断增加,所以只要清除条目就会导致映射越来越大。因为remove+add不是原子指令,所以您必须在映射本身上同步以确保原子性。你确定有多余的键+一个空列表会消耗太多内存吗?另外,Viktor Klang在multimap impl上的一个有趣的要点(因此,将iso a列表设置为值)->@maasg仍在处理它,现在我采用Vint的方法:不是删除,而是清除它。至于键太多的问题,我想也许我可以安排另一个线程来锁定整个地图,以便在给定的时间间隔内清理密钥。@maasg通过使用ReadWriteLock,最终解决问题,签出