Java ConcurrentHashMap上的竞争条件问题

Java ConcurrentHashMap上的竞争条件问题,java,multithreading,concurrency,thread-safety,race-condition,Java,Multithreading,Concurrency,Thread Safety,Race Condition,我得到了一个多线程应用程序,其中n个线程写入ConcurrentHashMap。另n个线程从该映射读取并将其值复制到复制列表中。 之后,原始列表将从地图中删除。 出于某种原因,我总是得到一个ConcurrentModificationException 我甚至尝试用一个易变的布尔值创建我自己的锁机制,但它不起作用。当将谷歌番石榴与列表一起使用时,我会得到一个ConcurrentModificationException。使用StandardWay新建链接列表(列表)时,我会得到一个BoundsE

我得到了一个多线程应用程序,其中n个线程写入
ConcurrentHashMap
。另n个线程从该映射读取并将其值复制到复制列表中。 之后,原始列表将从地图中删除。 出于某种原因,我总是得到一个
ConcurrentModificationException

我甚至尝试用一个易变的布尔值创建我自己的锁机制,但它不起作用。当将谷歌番石榴
列表一起使用时,我会得到一个
ConcurrentModificationException
。使用StandardWay
新建链接列表(列表)
时,我会得到一个
BoundsException数组

以下是编译代码示例:

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

}
公共类VolatileTest{
public static Map logMessages=new ConcurrentHashMap();
公共静态AtomicBoolean锁=新的AtomicBoolean(false);
公共静态void main(字符串[]args){
新线程(){
公开募捐{
while(true){
试一试{
如果(!VolatileTest.lock.get()){
VolatileTest.lock.set(真);
List=VolatileTest.logMessages.get(“测试”);
如果(列表!=null){
List copyList=集合。同步列表(List);
用于(字符串:复制列表){
System.out.println(字符串);
}
VolatileTest.logMessages.remove(“测试”);
}
VolatileTest.lock.set(假);
}
}捕获(ConcurrentModificationException例外){
例如printStackTrace();
系统出口(1);
}
}
};
}.start();
新线程(){
@凌驾
公开募捐{
while(true){
如果(!VolatileTest.lock.get()){
VolatileTest.lock.set(真);
List=VolatileTest.logMessages.get(“测试”);
if(list==null){
list=Collections.synchronizedList(新的LinkedList());
}
列表。添加(“测试错误”);
VolatileTest.logMessages.put(“测试”,列表);
VolatileTest.lock.set(假);
}
}
}
}.start();
}
是故障安全的,这意味着您不会遇到
ConcurrentModificationException
。这是映射中的
列表,其中一个线程尝试读取数据,而另一个线程在迭代时尝试删除数据

我建议,不要尝试锁定整个映射操作,而是要注意使线程安全地访问列表可能正在使用或


还要注意,如果(!VolatileTest.lock),则输入条件
{
对于这两个线程,这意味着它们可以同时运行,因为最初默认情况下,布尔值为false,并且两者可能同时在同一个列表上工作。

如前所述,锁定模式看起来无效。最好使用synchronized。下面的代码适合我

最终对象obj=新对象()

然后


synchronized(obj){….}而不是if(!VolatileTest.lock){….}

您有ConcurrentModificationException,因为您的锁定被破坏,读卡器线程同时读取写入器写入的相同列表(通过迭代器)

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

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
}
你的

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

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

因此:

  • 使用一个全局锁和普通HashMap/ArrayList
  • 移除全局锁,在列表的每个特定实例上使用ConcurrentHashMap和plain ArrayList with synchronized
  • 使用队列(某些BlockingQueue或ConcurrentLinkedQueue)而不是当前的所有内容
  • 使用Disruptor()之类的工具进行线程间通信,并提供许多选项

  • 既然我锁定了完整的写/读操作,为什么我会有任何问题?(目前用于测试目的)您的锁定模式不起作用,您需要对布尔值执行原子“检查并设置”操作(请参阅原子布尔值)。或用于相同目的的锁。嗨,请看我编辑的代码,我将volatile更改为AtomicBoolean。我还将列表更改为SynchronizedList,其中包含LinkedList,但仍不起作用。嗨,请看我编辑的代码,我将volatile更改为AtomicBoolean。我还将列表更改为SynchronizedList,其中包含LinkedList,但仍不起作用t工作。切换布尔值本身的实现并不能解决您的问题,即在线程检查
    布尔值和将同一布尔值设置为true之间可能会发生任何事情(这就是“您的检查和设置不是原子的”的意思).我提到AtomicBoolean是因为它在概念上可以解决这个问题(使用
    compareAndSet
    方法),但您必须正确使用它。正如@ramp所建议的,可读、高效、简洁且最不容易出错的方法是使用锁和/或同步。
    if (!VolatileTest.lock.get()) {
          VolatileTest.lock.set(true);
          ...
    }