java - ConcurrentHashMap 上的竞争条件问题

标签 java multithreading concurrency thread-safety race-condition

我有一个多线程应用程序,其中 n 个线程写入 ConcurrentHashMap。另外 n 个线程从该映射中读取并将其值复制到副本列表中。 之后,原始列表将从 map 中删除。 由于某种原因,我总是收到 ConcurrentModificationException

我什至尝试使用 volatile boolean 值创建自己的锁定机制,但它不起作用。当将 Google GuavaLists.newLinkedList() 一起使用时,我收到 ConcurrentModificationException。使用 StandardWay new LinkedList(list) 时,我收到 ArrayOutOfBoundsException

下面是编译代码示例:

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

}

最佳答案

您遇到 ConcurrentModificationException,因为您的锁定被破坏,并且读取器线程读取写入器同时写入的同一列表(通过迭代器)。

您的代码看起来像是无锁编码的尝试。如果是这样,您必须使用 CAS 操作,如下所示:

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
}

你的

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

不是原子的。或者您可以使用同步部分或任何其他标准锁定机制(例如 ReadWriteLock)

此外,如果您使用一个锁来处理列表的读写,则不必使用同步列表。而且,您甚至不需要 ConcurrentHashMap。

所以:

  1. 使用一个全局锁和普通 HashMap/ArrayList
  2. 移除全局锁,使用 ConcurrentHashMap 和普通 ArrayList,并在列表的每个特定实例上同步
  3. 使用队列(某些 BlockingQueue 或 ConcurrentLinkedQueue)代替当前的所有内容
  4. 使用 Disruptor ( http://lmax-exchange.github.io/disruptor/ ) 之类的工具进行与许多选项的线程间通信。另外,这里是如何构建无锁队列的一个很好的例子http://psy-lob-saw.blogspot.ru/2013/03/single-producerconsumer-lock-free-queue.html

关于java - ConcurrentHashMap 上的竞争条件问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28829462/

相关文章:

python - 多线程 python 脚本静默死亡 - 如何调试

java - ExecutorService awaitTermination 方法未按预期执行

java - bean 中的 Spring 构造函数注入(inject)。是 bean 了吗?

java - 显示器有问题

java - atomicLong 如果 atomic long 大于某个数字,是否可以进行 compareAndSet?

c# - 具有 1 个发布者和 4 个并行消费者的 Disruptor 示例

java - Android Set 上的 ConcurrentModificationException

java - Solaris:在应用程序的处理程序上安装文件系统

java - 如何创建非负整数正则表达式?

python - 套接字编程问题 - python