简体   繁体   中英

My ConcurrentHashmap's value type is List,how to make appending to that list thread safe?

My class extends from ConcurrentHashmap[String,immutable.List[String]]

and it has 2 methods :

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

in order to make the addEntry method thread safe,I tried :

this.get(key).synchronized{
  //append or set here
}

but that will raise null pointer exception if key does not exist,and use putIfAbsent(key, new immutable.List()) before synchronize won't work cause after putIfAbsent and before goes into synchronized block,the key may be removed by resetEntry.

make addEntry and resetEntry both synchronized method will work but the lock is too large

So, what could I do?

ps.this post is similiar with How to make updating BigDecimal within ConcurrentHashMap thread safe while plz help me figure out how to code other than general guide

--update-- checkout https://stackoverflow.com/a/34309186/404145 , solved this after almost 3+ years later.

Instead of removing the entry, can you simply clear it? You can still use a synchronized list and ensure atomicity.

  def resetEntry(key: String, currentBatchSize: Int): Unit = {
    this.get(key).clear();
  }

This works with the assumption that each key has an entry. For example if this.get(key)==null You would want to insert a new sychronizedList which should act as a clear as well.

After more than 3 years, I think now I can answer my question.

The original problem is:

I get a ConcurrentHashMap[String, List] , many threads are appending values to it, how can I make it thread-safe?

Make addEntry() synchronized will work, right?

synchronize(map.get(key)){
  map.append(key, value)
}

In most cases yes except when map.get(key) is null, which will cause NullPointerException.

So what about adding map.putIfAbsent(key, new List) like this:

map.putIfAbsent(key, new List)
synchronize(map.get(key)){
  map.append(key, value)
}

Better now, but if after putIfAbsent() another thread called resetEntry() , we will see NullPointerException again.

Make addEntry and resetEntry both synchronized method will work but the lock is too big.

So what about MapEntry Level Lock when appending and Map Level Lock when resetting?

Here comes the ReentrantReadWriteLock: When calling addEntry() , we acquire a share lock of the map, that makes appending as concurrently as possible, and when calling resetEntry() , we acquire an exclusive lock to make sure that no other threads are changing the map at the same time.

The code looks like this:

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()
  }
}

You can try a method inspired by the CAS (Compare and Swap) process:

(in pseudo-java-scala-code, as my Scala is still in its infancy)

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM