我正在使用在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 friends”。
我认为这是可行的,因为Java会对引用进行按值复制,但是我担心每隔1分钟重新分配阵列的生产应用可能有一天会崩溃。谢谢大家!
答案 0 :(得分:4)
您没有看到更新的列表内容,因为增强的 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]
其他几点:
您不应该在主线程中设置isReassigned
。分配列表后,必须对其进行设置。
正如@Kartik在评论中指出的那样,可能存在可见性问题,其他线程可能看不到所做的更改。标记列表和isReassigned
volatile 即可解决此问题。