我的类扩展自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 resetEntry(key: String): Unit = {
this.remove(key)
}
为了使addEntry方法线程安全,我尝试了:
this.get(key).synchronized{
//append or set here
}
但是如果key不存在则会引发空指针异常,并且在同步之前使用putIfAbsent(key,new immutable.List())将无效,因为在putIfAbsent之后并且在进入synchronized块之前,可能会删除该键通过resetEntry。
make addEntry和resetEntry两个synchronized方法都可以,但是锁定太大
那么,我该怎么办?
ps.t这篇文章与How to make updating BigDecimal within ConcurrentHashMap thread safe类似,而plz帮助我弄清楚如何编写除一般指南以外的代码
- update-- checkout https://stackoverflow.com/a/34309186/404145,在将近3年后解决了这个问题。
答案 0 :(得分:1)
您可以简单地清除它,而不是删除条目吗?您仍然可以使用同步列表并确保原子性。
def resetEntry(key: String, currentBatchSize: Int): Unit = {
this.get(key).clear();
}
这假设每个键都有一个条目。例如,如果this.get(key)==null
您希望插入一个新的sychronizedList
,它也应该是一个明确的。
答案 1 :(得分:1)
超过3年后,我想我现在可以回答我的问题了。
最初的问题是:
我得到一个ConcurrentHashMap[String, List]
,很多线程都在为它附加值,我怎样才能使它成为线程安全的呢?
让addEntry()
同步可以正常工作,对吗?
synchronize(map.get(key)){
map.append(key, value)
}
在大多数情况下是,除非map.get(key)
为空,否则将导致NullPointerException。
那么如何添加map.putIfAbsent(key, new List)
呢:
map.putIfAbsent(key, new List)
synchronize(map.get(key)){
map.append(key, value)
}
现在更好,但如果在putIfAbsent()
另一个名为resetEntry()
的线程之后,我们将再次看到NullPointerException。
make addEntry和resetEntry这两个synchronized方法都可以,但锁定太大了。
那么在附加时MapEntry Level Lock和重置时的Map Level Lock呢?
ReentrantReadWriteLock来了:
在调用addEntry()
时,我们获取了地图的共享锁,这使得尽可能同时追加,并且在调用resetEntry()
时,我们获取了一个独占锁,以确保没有其他线程正在更改地图在同一时间。
代码如下所示:
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()
}
}
答案 2 :(得分:0)
您可以尝试一种受CAS(比较和交换)过程启发的方法:
(在伪java-scala-code中,因为我的Scala还处于起步阶段)
def addEntry(key: String, newList: immutable.List[String]) = {
val existing = putIfAbsent(key, newList);
if (existing != null) {
synchronized(existing) {
if (get(key) == existing) { // ask again for the value within the synchronized block to ensure consistence. This is the compare part of CAS
return put(key,existing ++ newList); // Swap the old value by the new
} else {
throw new ConcurrentModificationException(); // how else mark failure?
}
}
}
return existing;
}