以下代码
ArrayList<String> foo, bar, test;
foo = new ArrayList<String>(Arrays.asList("foo"));
bar = new ArrayList<String>(Arrays.asList("bar"));
test = new ArrayList<String>(Arrays.asList("test"));
线程1运行:
foo = new ArrayList<String>(bar);
线程2运行:
bar = new ArrayList<String>(test);
如果线程2在线程1使用时更改bar
的值,是否会导致异常?
答案 0 :(得分:1)
您不会获得并发修改异常,因为列表实例永远不会发生变异。只交换引用。
但是你仍然有数据竞争:
bar
对象并创建一个新的`foo',它就是它的副本。bar
引用中的新列表,并且当Thread1稍后运行时,它将最终得到该自身的副本,是test
列表的副本。答案 1 :(得分:0)
根本不是问题。要了解发生了什么,您需要了解java如何处理ArrayList
(以及其他集合)等结构。
当您创建new ArrayList
变量时,该变量仅包含指向保留实际数据的RAM段的链接。将变量传递给构造函数或其他方法时,java
会复制变量的值,并使用此值初始化参数。因此,当您为参数指定新列表时,不会更改原始变量的值:
public class Main {
public static void main(String [] args) throws IOException {
List<String> foo, bar, test;
foo = new ArrayList<String>(Arrays.asList("foo"));
bar = new ArrayList<String>(Arrays.asList("bar"));
test = new ArrayList<String>(Arrays.asList("test"));
CountDownLatch cdl = new CountDownLatch(1);
Thread r1 = new Thread(new MyThread("foo = new ArrayList(bar)", bar, foo, cdl));
Thread r2 = new Thread(new MyThread("bar = new ArrayList(test)", test, bar, cdl));
r1.start();
r2.start();
cdl.countDown();
System.out.println("Size of foo is " + foo.size());
System.out.println("Size of bar is " + foo.size());
}
}
class MyThread implements Runnable {
private List<String> src;
private List<String> dst;
private CountDownLatch cdl;
private String msg;
public MyThread(String msg, List<String> src, List<String> dst, CountDownLatch cdl) {
this.msg = msg;
this.src = src;
this.dst = dst;
this.cdl = cdl;
}
@Override
public void run() {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
dst = new ArrayList<String>(src);
dst.add("test");
System.out.println(msg + " and my size is " + dst.size());
}
}
输出:
bar = new ArrayList(test) and my size is 2
foo = new ArrayList(bar) and my size is 2
Size of foo is 1
Size of bar is 1