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

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

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

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

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

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

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

示例

    @ThreadSafe
    public class BetterVector<E> extends Vector<E> {

        // 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<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;
        }
    }

正确示例

    @ThreadSafe
    class GoodListHelper<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;
            }
        }
    }

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


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

(4) __方法四: 组合__

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

示例

    @ThreadSafe
    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 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<? extends T> c) {
            return list.addAll(c);
        }

        public synchronized boolean addAll(int index, Collection<? extends T> 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<T> iterator() {
            return list.iterator();
        }

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

        public <T> 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<T> listIterator() {
            return list.listIterator();
        }

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

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

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

    定义好

    (1) 哪些变量为volatile

    (2) 哪些变量用锁保护

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

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

    等……

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,366评论 11 349
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,138评论 1 32
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,731评论 0 11
  • 今天是冬至,二十四节气中的一天。 这一天并没有什么不同,依旧是星期二,依旧早上九点起床,半小时后出门,买好早餐去上...
    冷汐阅读 315评论 0 0
  • 这是一个发生在我支教生涯里的真实故事。 事情是这样的,某个下午的英语课上,我在讲台上讲课,讲台下第二排的一位男生往...
    Steven文旭阅读 642评论 0 3