我有一个多线程应用程序,其中 n 个线程写入 ConcurrentHashMap
。另外 n 个线程从该映射中读取并将其值复制到副本列表中。
之后,原始列表将从 map 中删除。
由于某种原因,我总是收到 ConcurrentModificationException
。
我什至尝试使用 volatile boolean 值创建自己的锁定机制,但它不起作用。当将 Google Guava 与 Lists.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。
所以:
- 使用一个全局锁和普通 HashMap/ArrayList 或
- 移除全局锁,使用 ConcurrentHashMap 和普通 ArrayList,并在列表的每个特定实例上同步或
- 使用队列(某些 BlockingQueue 或 ConcurrentLinkedQueue)代替当前的所有内容或
- 使用 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/