xiaobaoqiu Blog

Think More, Code Less

Java Concurrency in Practice 4 : 组合对象

第四章:组合对象 我们希望通过将线程安全的组件以完全的方式组合成更大的组件或者程序.

1. 设计线程安全的类

设计线程安全类的过程应该包含下面3个基本要素: 1. 确定对象状态是由哪些变量构成的; 2. 确定限制状态变量的不变约束; 3. 制定一个管理并发访问对象状态的策略;

2. 实例限制

即使一个对象不是线程安全的,仍然有许多技术可以让他安全的用于多线程程序. 通过使用实例限制,封装简化了类的线程安全化工作.

将数据封装在对象内部,把对数据的访问限制在对象的方法上,更容易确保线程在访问数据时候总能获得正确的锁.

如下,HashSet是非线程安全的,但是由于mySet是private,不会逸出,因此HashSet被限制在PersonSet中,唯一可以访问mySet的代码路径是addPerson与containPerson,执行他们时都要获得PersonSet的锁.因而确保了PersonSet是线程安全的.

1
2
3
4
5
6
7
8
9
10
11
public class PersonSet {
    private final Set<Person> mySet = new HashSet<Person>();

    public synchronized void addPerson(Person p){
        mySet.add(p);
    }

    public synchronized boolean containPerson(Person p){
        return mySet.contains(p);
    }
}

java类库中有很多这样的例子.ArrayList和HashMap这样的基本容器类是非线程安全的,但是类库提供了包装器工厂方法,如:

1
2
3
Collections.synchronizedList(arrayList);
Collections.synchronizedMap(hashMap);
Collections.synchronizedSet(hashSet);

使得这些非线程安全的类可以安全的用于多线程环境.这些工厂方法的原理是使用Decorator模式,使用一个同步的包装器对象保证这些非线程安全的容器,将相关接口实现为同步方法,并将请求转发打下层容器对象上:

1
2
3
4
5
6
7
8
class SynchronizedCollection<E> implements Collection<E>, Serializable {
    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize
    ...
    public boolean add(E e) {
        synchronized(mutex) {return c.add(e);}
    }
    ...

3. 委托线程安全

java中CopyOnWrite系列(CopyOnWriteArrayList,CopyOnWriteArraySet),CopyOnWriteArrayList是ArrayList的线程安全版本,即实现了写时复制的ArrayList版本,写时复制即每次写操作都会触发一个复制(深拷贝)的过程,如CopyOnWriteArrayList的add操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    ...

    public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
    }
}

Collections中的Unmodifiable系列,包含UnmodifiableList,UnmodifiableMap,UnmodifiableSet,UnmodifiableSortedMap,UnmodifiableSortedSet,器特性是不支持对容器内数据的修改,比如包含UnmodifiableList:

1
2
3
4
5
6
7
8
9
static class UnmodifiableList<E> extends UnmodifiableCollection<E>
                      implements List<E> {
        static final long serialVersionUID = -283967356065247728L;
    final List<? extends E> list;
    ...
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
}

4. 向已有的线程安全类添加功能

  1. 可以直接扩展原始类的代码
  2. 通过继承来增加功能
  3. 扩展功能,而不是扩展类本身,将扩展代码置于一个助手类;
  4. 组合

Collections的synchronized系列容器,包括synchronizedList,synchronizedMap,synchronizedSet,synchronizedSortedMap,synchronizedSortedSet,是普通容器的线程安全版本版本,实现原理即每个操作上面都加锁:

1
2
3
4
5
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static class SynchronizedList<E>
    extends SynchronizedCollection<E>
    implements List<E> {
        static final long serialVersionUID = -7754090372962971524L;

    final List<E> list;

    public E get(int index) {
        synchronized(mutex) {return list.get(index);}
        }
    public E set(int index, E element) {
        synchronized(mutex) {return list.set(index, element);}
    }
}

和Vector类似,但是Vector通过给所有方法级别加上synchronized关键字修饰来实现所以线程安全.

助手类达到扩展功能的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ListHelper<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;
        }
    }
}

这种方式的问题是,将加锁的代码分布到对象继承体系中的多个类中,即类的行为和基类实现之间存在耦合.

向已有的线程安全类添加原子操作,更健壮的选择是组合.示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ImprovedList<T> implements List<T> {
    private final List<T> list;

    public ImprovedList(List<T> list){
        this.list = list;
    }

    public synchronized boolean putIfAbsent(T x){
        boolean absent = !list.contains(x);
        if(absent)
            list.add(x);
        return absent;
    }

    @Override
    public synchronized void clear() {
        list.clear();
    }
    //其他list方法的代理
}