Problems with race conditions on ConcurrentHashMap

◇◆丶佛笑我妖孽 提交于 2021-02-10 23:54:04

问题


I got a multithreaded application in which n threads write to an ConcurrentHashMap. Another n Threads read from that Map and copy its Value to a copy List. After that the original List is removed from the map. For some reason I always get a ConcurrentModificationException.

I even tried to create my own lock mechanism with a volatile boolean, but it won't work. When using Google Guava with Lists.newLinkedList() i get a ConcurrentModificationException. When using the StandardWay new LinkedList(list) I get an ArrayOutOfBoundsException.

Here is the compiling code example:

public class VolatileTest {

public static Map<String, List<String>> logMessages = new ConcurrentHashMap<String, List<String>>();

public static AtomicBoolean lock = new AtomicBoolean(false);

public static void main(String[] args) {
new Thread() {

  public void run() {
    while (true) {
      try {
        if (!VolatileTest.lock.get()) {
          VolatileTest.lock.set(true);
          List<String> list = VolatileTest.logMessages.get("test");
          if (list != null) {
            List<String> copyList = Collections.synchronizedList(list);
            for (String string : copyList) {
              System.out.println(string);
            }
            VolatileTest.logMessages.remove("test");
          }
          VolatileTest.lock.set(false);
        }
      } catch (ConcurrentModificationException ex) {
        ex.printStackTrace();
        System.exit(1);
      }
    }
  };
}.start();

new Thread() {

  @Override
  public void run() {
    while (true) {
      if (!VolatileTest.lock.get()) {
        VolatileTest.lock.set(true);
        List<String> list = VolatileTest.logMessages.get("test");
        if (list == null) {
          list = Collections.synchronizedList(new LinkedList<String>());
        }
        list.add("TestError");
        VolatileTest.logMessages.put("test", list);
        VolatileTest.lock.set(false);
      }
    }
  }
}.start();

}

回答1:


You have ConcurrentModificationException because you have your locking broken and the reader thread reads the same list (by Iterator) the writer writes to at the same time.

Your code looks like a try of lock-free coding. If so, you must use CAS operation like this:

while (!VolatileTest.lock.compareAndSet(false, true) { } // or while (VolatileTest.lock.getAndSet(true)) {} - try to get lock
try {
    // code to execute under lock
} finally {
    VolatileTest.lock.set(false); // unlock
}

Your

if (!VolatileTest.lock.get()) {
      VolatileTest.lock.set(true);
      ...
}

is not atomic. Or you can use synchronized section or any other standard locking mechanism (ReadWriteLock, for instance)

Also, if you deal with a list for reading and writing using one lock, you don't have to use synchronized list then. And moreover, you don't need even ConcurrentHashMap.

So:

  1. use one global lock and plain HashMap/ArrayList OR
  2. remove your global lock, use ConcurrentHashMap and plain ArrayList with synchronized on each particular instance of the list OR
  3. use a Queue (some BlockingQueue or ConcurrentLinkedQueue) instead of all of your current stuff OR
  4. use something like Disruptor (http://lmax-exchange.github.io/disruptor/) for inter-thread communication with many options. Also, here is a good example of how to build lock-free queues http://psy-lob-saw.blogspot.ru/2013/03/single-producerconsumer-lock-free-queue.html



回答2:


ConcurrentHashMap is fail safe meaning you will not encounter ConcurrentModificationException. It's your List<String> within the map where one of your thread tries to read the data while other thread is trying to remove the data while iterating.

I would suggest, you don't try locking on whole map operation, but instead look out for making thread safe access to list may be using Vector or SynchronizedList.

Also note, your entry condition if (!VolatileTest.lock) { for both the threads means they can both run at the same time as initially by default boolean would hold false value and both may try to work on same list at the same time.




回答3:


As already mentioned the locking pattern does not look valid. It is better to use synchronized. The below code works for me

final Object obj = new Object();

and then

synchronized (obj){....} instead of if (!VolatileTest.lock) {.....}



来源:https://stackoverflow.com/questions/28829462/problems-with-race-conditions-on-concurrenthashmap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!