我有一个多线程应用程序,其中n个线程写入ConcurrentHashMap
。另外n个线程从该Map读取并将其Value复制到副本List。
之后,原始列表将从地图中删除。
出于某种原因,我总是得到ConcurrentModificationException
。
我甚至尝试使用volatile布尔值创建自己的锁机制,但它不会起作用。将 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();
}
答案 0 :(得分:3)
你有ConcurrentModificationException,因为你的锁被破坏了,读者线程读取了写入者同时写入的相同列表(通过Iterator)。
您的代码看起来像是无锁编码的尝试。如果是这样,您必须使用 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);
...
}
不是原子的。或者您可以使用synchronized部分或任何其他标准锁定机制(例如,ReadWriteLock)
此外,如果您使用一个锁处理读写列表,则不必使用同步列表。此外,您甚至不需要ConcurrentHashMap。
所以:
答案 1 :(得分:0)
ConcurrentHashMap失败安全意味着您不会遇到ConcurrentModificationException
。它是您在地图中的List<String>
,其中一个线程尝试读取数据,而其他线程在迭代时尝试删除数据。
我建议,您不要尝试锁定整个地图操作,但要注意使用线程安全访问列表可能正在使用Vector或SynchronizedList。
另请注意,两个线程的条目if (!VolatileTest.lock) {
意味着它们都可以在最初的同时运行,默认情况下,boolean将保持false值,并且两者可能同时尝试在同一列表上工作。
答案 2 :(得分:0)
如前所述,锁定模式看起来不合适。最好使用synchronized。以下代码适用于我
final Object obj = new Object();
然后
synchronized(obj){....}而不是if(!VolatileTest.lock){.....}