在 get() 时,如果多个线程尝试从列表中获取它们将没有问题。因为由于易失性数组,它总是会读取最新的副本并从数组中返回元素。
但
在 add() 或 set() 期间,每次他们创建一个新数组以避免相互执行问题,这是使对象线程安全以使其不可变的一种方法。
如果他们在添加或设置期间使用了相同的数组对象,那么他们必须使遍历同步。或者如果任何线程在遍历期间添加/删除要列出的对象,它可能会抛出异常
根据 java 文档
java.util.ArrayList 的线程安全变体,其中所有可变操作(添加、设置等)都是通过制作底层数组的新副本来实现的。
这通常成本太高,但是当遍历操作的数量远远超过突变时,它可能比替代方法更有效,并且在您不能或不想同步遍历时很有用
看到这个
package com.concurrent;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListTest {
/**
* @param args
*/
public static void main(String[] args) {
CopyOnWriteArrayList<Integer> list=new CopyOnWriteArrayList<>();
Viewer viewer=new Viewer();
viewer.setList(list);
Thread t1=new Thread(viewer);
Adder adder=new Adder();
adder.setList(list);
Thread t=new Thread(adder);
t.start();
t1.start();
}
static class Adder implements Runnable{
private List<Integer> list;
public void setList(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
for(int i=0;i<100;i++){
list.add(i);
System.out.println("Added-"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Viewer implements Runnable{
private List<Integer> list;
public void setList(List<Integer> list) {
this.list = list;
}
@Override
public void run() {
while (true) {
System.out.println("Length of list->"+list.size());
for (Integer i : list) {
System.out.println("Reading-"+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}