我的ConcurrentHashmap值类型是List,如何附加到该列表线程安全?

时间:2012-07-16 15:40:50

标签: 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 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年后解决了这个问题。

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;
}