因为ArrayList本身不是线程安全的,通过Collections.synchronizedList可以将其包装成一个线程安全的List。
例如:
//这句话其实是产生一个新的对象,
List list = Collections.synchronizedList(new ArrayList());
1、方法源码
//来看看这个函数
public class Collections {
//上面省略了很多源码....
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}
//省略源码....
}
这里有两个内部类,SynchronizedRandomAccessList和SynchronizedList
SynchronizedRandomAccessList实现了RandomAccess接口,
下面我们来看SynchronizedList
/**
* @serial include
*/
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
//可以看到所有的操作都是上了锁的,synchronized (mutex),锁对象是mutex是来自SynchronizedCollection父类
//省略...
public boolean equals(Object o) {
if (this == o)
return true;
synchronized (mutex) {return list.equals(o);}
}
public int hashCode() {
synchronized (mutex) {return list.hashCode();}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
//省略...
}
创建出SynchronizedList之后,再操作该list时,就会将方法上锁,实现线程安全
2、没有做同步的地方
但是有一个地方很值得注意的是迭代器的使用是没有上锁的,而且源码是标注了,需要用户去自己做同步处理, 迭代器,分割,流操作都需要自己实现同步处理。
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
//省略了做了同步的操作和一些源码......
public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!需要用户手动的去实现同步
}
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!需要用户手动的去实现同步
}
@Override
public Stream<E> stream() {
return c.stream(); // Must be manually synched by user!需要用户手动的去实现同步
}
@Override
public Stream<E> parallelStream() {
return c.parallelStream(); // Must be manually synched by user!需要用户手动的去实现同步
}
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
//省略了做了同步的操作......
}
synchronizedList在迭代的时候,需要开发者自己加上线程锁控制代码,因为在整个迭代的过程中如果在循环外面不加同步代码,在一次次迭代之间,其他线程对于这个容器的add或者remove会影响整个迭代的预期效果,所以这里需要用户在整个循环外面加上synchronized(list);
例如:
public class SynchronizedListTest {
public static void main(String[] args) {
// 创建一个List数组
List<String> lists = new ArrayList<String>();
// 添加元素
lists.add("1");
lists.add("2");
lists.add("3");
// 创建一个synchronizedList
List<String> synlist = Collections.synchronizedList(lists);
// 迭代集合元素
synchronized (lists) {
//获取迭代器
Iterator<String> iterator = synlist.iterator();
//遍历
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
}