这段Java代码线程安全吗? [英] Is this piece of Java code thread safe?

查看:41
本文介绍了这段Java代码线程安全吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

}

我们的部分产品代码是这样的,主线程和子线程共享mainList.我运行了测试服务时间,但从未重现 ConcurrentModificationException.

Part of our product code likes this, the main thread and subthread share the mainList. I run the test serval times but never reproduce the ConcurrentModificationException.

更新:

感谢大家的回复,这段代码其实是我们生产代码的一个简单抽象.其实我想做的很简单:

thanks for all your replying,this code is actually a brief abstraction of our production code. What I wanna do actually is very simply:

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

也许更安全的方法是提取

Maybe a more safer way to accomplish this is to extract the

List<Integer> tempList = mainList;
mainList = new ArrayList<Integer>();

到主线程,并将templist传递给子线程.我之前列出的代码是遗留代码,我想修复这段代码.

to the main thread, and pass the templist to sub thread. The code I list before is a legacy code, and I want to fix this code.

推荐答案

正如 David Wallace 所指出的,您至少需要将 mainList 声明为 volatile.

As David Wallace points out, you need to at least declare mainList as volatile.

然而,仅凭这一点实际上并不能使代码成为线程安全的.即使您在 cmd 线程中切换引用,主线程可能已经在此之前获取了引用,然后可以在 cmd 的同时继续处理它 线程从中读取.

However, that alone does not actually make the code thread-safe. Even though you're switching the reference in the cmd thread, the main thread may have already fetched the reference before that happens and can then proceed to work on it at the same time as the cmd thread reads from it.

例如,这是一个可能的事件序列:

For example, this is a possible sequence of events:

  1. cmd 线程获取 mainList 引用并获取列表 A
  2. 主线程获取mainList 引用并获取列表A
  3. cmd 线程创建新列表 B,并将其分配给 mainList
  4. 主线程开始计算一个随机数
  5. cmd 线程开始遍历列表 A
  6. 主线程将其随机数添加到列表 A
  7. cmd 线程继续迭代修改后的列表,现在由于并发修改而处于不一致状态
  1. cmd thread fetches mainList reference and gets list A
  2. Main thread fetches mainList reference and also gets list A
  3. cmd thread creates the new list, B, and assigns it to mainList
  4. Main thread starts calculating a random number
  5. cmd thread starts iterating over list A
  6. Main thread adds its random number to list A
  7. cmd thread continues to iterate over the modified list, now in an inconsistent state due to concurrent modification

编辑:此时,我正计划在建议中进行编辑以执行您想要的操作,但我意识到您可能希望使用此代码做几件完全不同的事情,所以无论如何,一个建议只是一个猜测.如果您想要一个解决方案,我建议您开始一个新问题,更详细地描述您的目标.

EDIT: At this point, I was planning to edit in a suggestion to do what you wanted, but I realized there could be several quite different things that you wanted to do with this code, so a single suggestion would just be a guess anyway. If you want a solution, I suggest you start a new question describing your goal in more detail.

这篇关于这段Java代码线程安全吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆