这段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代码是线程安全的吗? 公共类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&

}

部分产品代码如下所示,主线程和子线程共享mainList。我多次运行测试,但从未复制ConcurrentModificationException

更新:

感谢您的回复,这段代码实际上是我们产品代码的一个简短抽象。其实我想做的很简单:

主线程持有一个列表以接收来自某个源的数据,当列表达到一定大小时,主线程将该列表传递给一个子线程,该子线程将数据存储到数据库中。

也许更安全的方法是提取

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