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