Poison

Collections.SynchronizedList

当多线程并发访问一个 List 的实例时,可以使用 Collections.synchronizedList(List<T>) 将 List 的实例进行包装,其内部调用的构造函数位于 Collections.java at jdk8-b120:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static class SynchronizedCollection<E> implements Collection<E>, Serializable {

final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize

SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}

SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}

public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
}

public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}

// ...
}

可以看出内部使用了 mutex 作为锁的对象以保证线程安全,而为什么不直接在方法上加上 synchronized 以实现相同的语义呢?是因为第二个构造函数允许用户传入锁的对象,比如用户需要使用单个锁来同步多个集合时,以实现对多集合多线程的并发访问。

Collections.synchronizedList(List<T>) 方法的 Java Doc 中还提到,使用迭代器时需要在外部加锁。

It is imperative that the user manually synchronize on the returned list when iterating over it:

1
2
3
4
5
6
7
List list = Collections.synchronizedList(new ArrayList());
...
synchronized (list) {
Iterator i = list.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}

Failure to follow this advice may result in non-deterministic behavior.

Reference

Why does SynchronizedCollection assign this to a mutex? - Stack Overflow