以Collections.synchronizedList(List<T> list)为例来讲一下如何将非线程安全的集合转为线程安全的集合。
Collections.synchronizedList源码如下:(注意这里:静态资源不认识范型,所以需要<T>来声明一下范型)
public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list) : new SynchronizedList<>(list)); } static <T> List<T> synchronizedList(List<T> list, Object mutex) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList<>(list, mutex) : new SynchronizedList<>(list, mutex)); }
如果是Collections.synchronizedList(ArrayList<T> arrayList),arrayList 实现了 RandomAccess,所以会返回SynchronizedRandomAccessList(arrayList)
来看SynchronizedRandomAccessList(arrayList)的源码:
static class SynchronizedRandomAccessList<E> extends SynchronizedList<E> implements RandomAccess { SynchronizedRandomAccessList(List<E> list) { super(list); } SynchronizedRandomAccessList(List<E> list, Object mutex) { super(list, mutex); } public List<E> subList(int fromIndex, int toIndex) { synchronized (mutex) { return new SynchronizedRandomAccessList<>( list.subList(fromIndex, toIndex), mutex); } } private static final long serialVersionUID = 1530674583602358482L; /** * Allows instances to be deserialized in pre-1.4 JREs (which do * not have SynchronizedRandomAccessList). SynchronizedList has * a readResolve method that inverts this transformation upon * deserialization. */ private Object writeReplace() { return new SynchronizedList<>(list); }
在SynchronizedRandomAccessList类中,通过锁mutex,来实现ArrayList的线程安全性。而这个mutext在父类中,来看SynchronizedList源码:
static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> { private static final long serialVersionUID = -7754090372962971524L; final List<E> list; SynchronizedList(List<E> list) { super(list); this.list = list; } SynchronizedList(List<E> list, Object mutex) { super(list, mutex); this.list = list; } 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);} }
在这个类中没有mutext,那么再向上找,以下是SynchronizedCollection源码:
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 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 int size() { synchronized (mutex) {return c.size();} } public boolean isEmpty() { synchronized (mutex) {return c.isEmpty();} } public boolean contains(Object o) { synchronized (mutex) {return c.contains(o);} }
在这个类中,可以清楚地看到mutext = this, 所以SynchronizedRandomAccessList(arrayList)通过自身锁,来实现线程安全性。
如果传入的类是LinkedList,没有实现RandomAccess接口,那就返回SynchronizedList,可以看到SynchronizedList也是通过锁mutext,使LinkedList操作实现线程安全性。
所以如果直接使用Collections.synchronizedList(List list) 的操作,那都是原子的,但是如果要为这个新的list重新添加一些操作,就需要注意锁对象的一致性。比如下边这个例子:
package com.util.concurrent; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BadListHelper<E> { public List<E> list = Collections.synchronizedList(new ArrayList<E>()); public synchronized boolean putIfAbsent(E x) { boolean absent = !list.contains(x); if (absent) { list.add(x); } return absent; } }
虽然list已经是线程安全的集合,但是在做putIfAbsent时,还是无法阻止其他线程修改list内容,因为putIfAbsent锁的是BadListHelper这个对象,而list的锁是list对象。
正确的做法是:
package com.util.concurrent; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class BadListHelper<E> { public List<E> list = Collections.synchronizedList(new ArrayList<E>()); public boolean putIfAbsent(E x) { synchronized (list) { boolean absent = !list.contains(x); if (absent) { list.add(x); } return absent; } } }