0%

结合SynchronizedList学习使用实例封闭实现线程安全的类

在java并发编程实战的4.2节“实例封闭”里介绍了通过封闭机制确保线程安全,这里结合 java SynchronizedList 来了解下这一实现过程。

当需要将一个 List 包装为线程安全时,可以如下调用:

1
2
List l = new LinkedList();
List l2 = Collections.synchronizedList(l);

之后通过 l2 对 l 的一切操作都将是线程安全的。

看一下这一方法:

1
2
3
4
5
public static <T> List<T> synchronizedList(List<T> list) {
return (list instanceof RandomAccess ?
new SynchronizedRandomAccessList<>(list) :
new SynchronizedList<>(list));
}

这里我们传入的是一个 LinkedList,那么赋值给 l2 的返回值应当是一个 SynchronizedList 的实例。如果传入的是一个 ArrayList,那么将返回 SynchronizedRandomAccessList 的实例,这是 SynchronizedList 的一个子类,是同样的处理方式。

看下 SynchronizedList。首先,在初始化函数中,保存传入的参数到 final 域 list 中,

1
2
3
4
5
6
final List<E> list;

SynchronizedList(List<E> list) {
super(list);
this.list = list;
}

而 SynchronizedList 中所有访问 list 的方法都使用同一个对象 mutex 进行了加锁,比如 hashCode() 方法:

1
2
3
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}

mutex 是其父类 SynchronizedCollection 的一个域,顾名思义,用于加锁,

1
final Object mutex;     // Object on which to synchronize

SynchronizedList 是实现了 List 接口的,那么 List 接口的方法呢?

可以看到,SynchronizedList 的父类 SynchronizedCollection 实现了 Collection 接口(List 的父类),而 List 接口中添加的方法在 SynchronizedList 也得到了实现,无一例外,这些方法都使用了 mutex 加锁进行了同步。

这是一个装饰者模式的例子,对 l 的所有操作被封装到了 SynchronizedList 中,都进行了同步,使得 l 指向的容器成为了一个线程安全的对象。在使用封装的情况下,对 l 对象的所有访问的代码路径都是容易发现和控制的。

但是 SynchronizedList 并不是使l完全的线程安全了,正如 java 并发编程实践所说,如果包装器拥有对容器对象的唯一引用,那么可以确保容器对象 l 是线程安全的,而不能保证是唯一引用时,外部如果要正确的作为线程安全对象使用,对l容器的一切操作都应该通过包装器进行。

举一个例子如下:

1
2
3
4
List l = new LinkedList<>();
List l2 = Collections.synchronizedList(l);
l.add(new Object());
l2.add(new Object());

打个断点,Force Step Into 一下可以发现,以上代码中,l.add(new Object()) 调用 LinkedList类的add(E e) 方法;l2.add(new Object()) 则调用了 SynchronizedCollection 类的 add(E e) 方法,是通过 SynchronizedList 类的代理来实现的,是加锁同步了的。也就是说,我们在对容器对象l进行包装前保留了它的一个引用 l,那么对其进行包装后,我们仍然可以通过 l 不带同步的访问原容器对象。

所以在 synchronizedCollection 类的注释里强调:

In order to guarantee serial access, it is critical that all access to the backing collection is accomplished through the returned collection.