【问题标题】:Is it thread safe to reassign a list in Java?在 Java 中重新分配列表是否是线程安全的?
【发布时间】:2019-02-12 04:24:40
【问题描述】:

我正在使用在 ArrayList 上运行的两个线程。我知道您不能在迭代时修改列表,但 Java 让我在循环中重新分配(替换)整个列表,没有问题。这是线程安全的吗?

public class Main {

    static List<String> list = Arrays.asList("hi", "there", "friend");

    public static void main(String... args) throws InterruptedException {

        boolean isReassigned = false;

        for (String s: list) {
            System.out.println(s);
            if (!isReassigned) {
                new Thread(() -> reassignList()).start();
                isReassigned = true;
            }
            Thread.sleep(1000);
        }
    }

    public static void reassignList() {
        System.out.println("Reassigning list....");
        list = Arrays.asList("goodbye", "old", "list");
    }
}

我预计在“hi”之后会出现并发问题,但我得到了“hi there friend”。

我认为这是可行的,因为 Java 会在引用上按值复制,但我担心我的生产应用程序每 1 分钟重新分配一次数组可能有一天会崩溃。谢谢大家!

【问题讨论】:

  • 设为volatile,你应该会没事的。

标签: java multithreading list


【解决方案1】:

您看不到更新后的列表内容,因为增强的 for 循环在后台使用了迭代器。因此,您正在使用迭代器(到旧列表)来迭代数据(即使在分配完成之后)。这意味着这两个列表都在范围内(而不是垃圾收集)。曾经,isReassigned 为真,如果你得到一个指向 list 的迭代器,你就可以看到新的内容。

我稍微修改了你的代码(添加了一个 else 块)来理解这一点

for (String s: list) {
    System.out.println(s);
    if (!isReassigned) {
        new Thread(() -> reassignList()).start();
        isReassigned = true;
    } else {
        System.out.println("Latest data " + list);
    }
    Thread.sleep(1000);
 }

这会产生

hi
Reassigning list....
there
Latest data [goodbye, old, list]
friend
Latest data [goodbye, old, list]

其他几点:

  1. 您不应该在主线程中设置isReassigned。必须在分配列表后设置。

  2. 正如@Kartik 在 cmets 中指出的那样,可能存在可见性问题,其他线程可能看不到所做的更改。标记列表和isReassigned volatile 将解决这个问题。

【讨论】:

  • (+1) 更改为常规 for 循环输出 OP 所期望的。但是,在列表被标记为 volatile 或使用某种同步之前,它是不可靠的,因为列表的新引用可能不会对其他线程“可见”。
猜你喜欢
  • 1970-01-01
  • 2021-12-30
  • 2015-09-29
  • 2018-05-07
  • 1970-01-01
  • 1970-01-01
  • 2011-09-03
相关资源
最近更新 更多