这段Java代码是线程安全的吗? 公共类TestConcurrentForList{ List mainList=new ArrayList(); ScheduledExecutorService ScheduledExecutorService=执行者。newScheduledThreadPool(1); 随机r=新随机(); public void start()引发InterruptedException{ Runnable cmd=new Runnable(){ @凌驾 公开募捐{ List templast=mainList; mainList=newarraylist(); for(整数i:圣殿骑士){ System.out.println(“子线程:+i”); } } }; scheduledExecutorService.scheduleAtFixedRate(cmd,1,1,TimeUnit.ms); while(true){ 主列表。添加(r.nextInt(200)); 睡眠(100); } } 公共静态void main(字符串[]args){ TestConcurrentForList测试仪=新建TestConcurrentForList(); 试一试{ tester.start(); }捕获(例外e){ e、 printStackTrace(); System.err.println(e.getMessage()); } }
} 部分产品代码如下所示,主线程和子线程共享mainList。我多次运行测试,但从未复制ConcurrentModificationException 更新: 感谢您的回复,这段代码实际上是我们产品代码的一个简短抽象。其实我想做的很简单: 主线程持有一个列表以接收来自某个源的数据,当列表达到一定大小时,主线程将该列表传递给一个子线程,该子线程将数据存储到数据库中。 也许更安全的方法是提取这段Java代码是线程安全的吗? 公共类TestConcurrentForList{ List mainList=new ArrayList(); ScheduledExecutorService ScheduledExecutorService=执行者。newScheduledThreadPool(1); 随机r=新随机(); public void start()引发InterruptedException{ Runnable cmd=new Runnable(){ @凌驾 公开募捐{ List templast=mainList; mainList=newarraylist(); for(整数i:圣殿骑士){ System.out.println(“子线程:+i”); } } }; scheduledExecutorService.scheduleAtFixedRate(cmd,1,1,TimeUnit.ms); while(true){ 主列表。添加(r.nextInt(200)); 睡眠(100); } } 公共静态void main(字符串[]args){ TestConcurrentForList测试仪=新建TestConcurrentForList(); 试一试{ tester.start(); }捕获(例外e){ e、 printStackTrace(); System.err.println(e.getMessage()); } },java,multithreading,thread-safety,Java,Multithreading,Thread Safety,} 部分产品代码如下所示,主线程和子线程共享mainList。我多次运行测试,但从未复制ConcurrentModificationException 更新: 感谢您的回复,这段代码实际上是我们产品代码的一个简短抽象。其实我想做的很简单: 主线程持有一个列表以接收来自某个源的数据,当列表达到一定大小时,主线程将该列表传递给一个子线程,该子线程将数据存储到数据库中。 也许更安全的方法是提取 public class TestConcurrentForList { List<Integer&
public class TestConcurrentForList {
List<Integer> mainList = new ArrayList<Integer>();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Random r = new Random();
public void start() throws InterruptedException {
Runnable cmd = new Runnable() {
@Override
public void run() {
List<Integer> tempList = mainList;
mainList = new ArrayList<Integer>();
for (Integer i: tempList) {
System.out.println("subThread:" + i);
}
}
};
scheduledExecutorService.scheduleAtFixedRate(cmd, 1, 1, TimeUnit.MILLISECONDS);
while (true) {
mainList.add(r.nextInt(200));
Thread.sleep(100);
}
}
public static void main(String[] args) {
TestConcurrentForList tester = new TestConcurrentForList();
try {
tester.start();
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
}
}
List templast=mainList;
mainList=newarraylist();
到主线程,并将模板传递给子线程。我前面列出的代码是旧代码,我想修复此代码。不,它不是线程安全的。您没有在
mainList
周围使用任何同步功能。代码没有抛出ConcurrentModificationException
这一事实并不意味着代码是线程安全的。这仅仅意味着如果抛出竞争条件,您可能会遇到竞争条件。不,我认为该代码不是线程安全的,因为在池线程向mainList分配新值时,主线程可以调用List.add。如果mainList中有灵长类动物,可能足以使其“不稳定”。但我认为不能将“volatile”与对象引用一起使用
为了确保分配的安全性,您需要在分配时同步某些内容,然后在尝试触摸mainList的任何位置同步,如:
List<Integer> tempList = mainList;
mainList = new ArrayList<Integer>();
对象锁=新对象();
...
已同步(锁定){
mainList=newarraylist();
}
...
已同步(锁定){
主列表。添加(r.nextInt(200));
}
这将确保当主线程正在调用add()时,池线程无法重新分配mainList
但是我不确定如果只有主线程修改列表,而池线程只遍历元素,是否可以得到ConcurrentModificationException。即使池线程确实修改了列表,我仍然不确定如果池线程修改了一个尚未分配给mainList的新列表,您是否可以获得CME
因此,如果您看到的是CME,我怀疑您的测试并不能真正代表生产中正在发生的情况。正如David Wallace指出的,您至少需要将
mainList
声明为volatile
然而,仅此一点并不能真正使代码线程安全。即使您在cmd
线程中切换引用,主线程也可能已经获取了该引用,然后可以在cmd
线程读取该引用的同时继续处理该引用
例如,这是一个可能的事件序列:
cmd
线程获取mainList
引用并获取列表mainList
引用,还获取列表cmd
线程创建新列表B,并将其分配给mainList
cmd
线程开始在列表A上迭代cmd
线程继续迭代已修改的列表,由于并发修改,现在处于不一致状态编辑:此时,我正计划编辑一个建议来做您想要做的事情,但我意识到您可能想用这段代码做一些完全不同的事情,所以一个建议只是一个猜测。如果您想要一个解决方案,我建议您开始一个新问题,更详细地描述您的目标。您应该明确地将
mainList
声明为volatile
;否则,这可能无法达到您的目的。@DavidWallace要澄清的是,volatile
关键字用于指示变量的值将由不同的线程修改。使变量volatile
不影响代码是否被视为“线程安全”。它不是线程安全的,因为当您有多个线程时,mainList上会出现ConcurrentModificationException
。添加(r.nextInt(200))
@AlexLockwood在大多数情况下,我同意您的意见,但在这种特殊情况下,缺少volatile
是导致此代码不具有线程安全性的原因。如果你仔细研究代码,你就会明白为什么。说明这个程序应该实现什么可能会很有用。一点也不清楚,因为在“cmd”线程中新列表的分配很奇怪。我认为这不太正确。一次只有一个线程使用mainList
,因此不需要同步它。唯一阻止它成为线程安全的是mainList
没有声明为volatir
Object lock = new Object();
...
synchronized (lock) {
mainList = new ArrayList<Integer>();
}
...
synchronized (lock) {
mainList.add(r.nextInt(200));
}