1_基础知识_chapter04_对象的组合_4_在现有的线程安全类中添加功能

  • 很多情况下应该重用现有的类, 添加自定义的功能, 此时需要在不破坏线程安全性的情况下添加新的操作

    (1) 方法一: 直接修改原始的类

    优点: 同步策略仍然处于同一个源代码文件中, 更容易理解和维护

    缺点: 常常无法修改源代码

    (2) 方法二: 扩展这个类

    同步策略分布到了各个文件中, 并且要确定的得知基类的同步策略

示例

    @ThreadSafe
    public class BetterVector extends Vector {

        // When extending a serializable class, you should redefine serialVersionUID
        static final long serialVersionUID = -3963416950630760754L;

        public synchronized boolean putIfAbsent(E x) {

            boolean absent = !super.contains(x);
            if (absent) {
                super.add(x);
            }

            return absent;
        }
    }

(3) __方法三: 客户端加锁机制__

扩展类的功能, 但不是扩展类本身, 而是将扩展代码放入__辅助类__中

__客户端加锁__: 对于使用某个对象X的客户端代码, 使用__X本身用于保护其状态的锁__来保护这段客户代码

__所以, 使用客户端加锁方式, 必须从源码中找到X使用的是哪一个锁__


示例

错误示例

    @NotThreadSafe
    class BadListHelper {

        public List list = Collections.synchronizedList(new ArrayList());

        public synchronized boolean putIfAbsent(E x) {

            boolean absent = !list.contains(x);

            if (absent) {
                list.add(x);
            }

            return absent;
        }
    }

正确示例

    @ThreadSafe
    class GoodListHelper {

        public List list = Collections.synchronizedList(new ArrayList());

        public boolean putIfAbsent(E x) {

            synchronized (list) {
        
                boolean absent = !list.contains(x);

                if (absent) {
                    list.add(x);
                }

                return absent;
            }
        }
    }

正确示例和错误示例的区别在于使用锁的不同, 深入Collections.synchronizedList的源码, 会发现它使用的锁就是Collections.synchronizedList对象本身, 所以辅助类中也要使用Collections.synchronizedList本身作为锁


和方法二一样, 客户端加锁方式也存在同步策略分布在各个类中的问题, 这样当底层源码的同步策略改变时可能会不稳。

(4) __方法四: 组合__

__使用Java监视器模式, 对内部的对象完全由类本身提供的锁保护, 不管底层的类是否线程安全__

示例

    @ThreadSafe
    public class ImprovedList implements List {

        private final List list;

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

        public synchronized boolean putIfAbsent(T x) {

            boolean contains = list.contains(x);
            if (contains) {
                list.add(x);
            }

            return !contains;
        }

        // Plain vanilla delegation for List methods.
        // Mutative methods must be synchronized to ensure atomicity of putIfAbsent.

        public synchronized boolean addAll(Collection c) {
            return list.addAll(c);
        }

        public synchronized boolean addAll(int index, Collection c) {
            return list.addAll(index, c);
        }

        public synchronized boolean removeAll(Collection c) {
            return list.removeAll(c);
        }

        public synchronized boolean retainAll(Collection c) {
            return list.retainAll(c);
        }

        public synchronized void clear() {
            list.clear();
        }

        public synchronized boolean add(T e) {
            return list.add(e);
        }

        public synchronized boolean remove(Object o) {
            return list.remove(o);
        }


        public int size() {
            return list.size();
        }

        public boolean isEmpty() {
            return list.isEmpty();
        }

        public boolean contains(Object o) {
            return list.contains(o);
        }

        public Iterator iterator() {
            return list.iterator();
        }

        public Object[] toArray() {
            return list.toArray();
        }

        public  T[] toArray(T[] a) {
            return list.toArray(a);
        }

        public boolean containsAll(Collection c) {
            return list.containsAll(c);
        }

        public boolean equals(Object o) {
            return list.equals(o);
        }

        public int hashCode() {
            return list.hashCode();
        }

        public T get(int index) {
            return list.get(index);
        }

        public T set(int index, T element) {
            return list.set(index, element);
        }

        public void add(int index, T element) {
            list.add(index, element);
        }

        public T remove(int index) {
            return list.remove(index);
        }

        public int indexOf(Object o) {
            return list.indexOf(o);
        }

        public int lastIndexOf(Object o) {
            return list.lastIndexOf(o);
        }

        public ListIterator listIterator() {
            return list.listIterator();
        }

        public ListIterator listIterator(int index) {
            return list.listIterator(index);
        }

        public List subList(int fromIndex, int toIndex) {
            return list.subList(fromIndex, toIndex);
        }
    }

所有需要同步的方法都使用ImprovedList本身的锁, 而不必在意封装对象的同步策略
  • 同步策略应该文档化

    定义好

    (1) 哪些变量为volatile

    (2) 哪些变量用锁保护

    (3) 哪些变量必须不可变或者线程封闭

    (4) 哪些操作必须是原子操作

    等……

你可能感兴趣的