搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > ApeClub > 说说线程安全包装:Collections.synchronizedList

说说线程安全包装:Collections.synchronizedList

ApeClub 2019-04-09
举报

前言

Java集合工具类Collections.synchronizedList提供了集合的线程安全包装方法。那么它是如何让一个集合变成线程安全的呢?为什么说这种线程安全集合的实现效率非常低下?

synchronizedList的实现

我们来看源码吧:

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

    /**
     * @serial include
     */

    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);}
        }
        public E set(int index, E element) {
            synchronized (mutex) {return list.set(index, element);}
        }
        public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E remove(int index) {
            synchronized (mutex) {return list.remove(index);}
        }

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

        public boolean addAll(int index, Collection<? extends E> c) {
            synchronized (mutex) {return list.addAll(index, c);}
        }

        public ListIterator<E> listIterator() {
            return list.listIterator(); // Must be manually synched by user
        }

        public ListIterator<E> listIterator(int index) {
            return list.listIterator(index); // Must be manually synched by user
        }

        public List<E> subList(int fromIndex, int toIndex) {
            synchronized (mutex) {
                return new SynchronizedList<>(list.subList(fromIndex, toIndex),
                                            mutex);
            }
        }

        @Override
        public void replaceAll(UnaryOperator<E> operator) 
{
            synchronized (mutex) {list.replaceAll(operator);}
        }
        @Override
        public void sort(Comparator<? super E> c) 
{
            synchronized (mutex) {list.sort(c);}
        }

        /**
         * SynchronizedRandomAccessList instances are serialized as
         * SynchronizedList instances to allow them to be deserialized
         * in pre-1.4 JREs (which do not have SynchronizedRandomAccessList).
         * This method inverts the transformation.  As a beneficial
         * side-effect, it also grafts the RandomAccess marker onto
         * SynchronizedList instances that were serialized in pre-1.4 JREs.
         *
         * Note: Unfortunately, SynchronizedRandomAccessList instances
         * serialized in 1.4.1 and deserialized in 1.4 will become
         * SynchronizedList instances, as this method was missing in 1.4.
         */

        private Object readResolve() {
            return (list instanceof RandomAccess
                    ? new SynchronizedRandomAccessList<>(list)
                    : this);
        }
    }

当synchronizedList传入的参数类型是ArrayList时, 因为ArrayList实现了RandomAccess接口,所以synchronizedList会构建一个SynchronizedRandomAccessList对象,不过没关系,SynchronizedList是SynchronizedRandomAccessList的父类,我们直接看他的实现。

list 对象直接维护了传递进来的参数List类型参数,而在get set add remove等方法中的实现都用线程同步语句块 synchronized (mutex)封装起来。那么mutex这把锁是谁呢? 看起来要到super(list);里面去找了.这时候就来到了SynchronizedList的父类SynchronizedCollection:

    /**
     * @serial include
     */

    static class SynchronizedCollection<Eimplements 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);}
        }
        public Object[] toArray() {
            synchronized (mutex) {return c.toArray();}
        }
        public <T> T[] toArray(T[] a) {
            synchronized (mutex) {return c.toArray(a);}
        }

        public Iterator<E> iterator() {
            return c.iterator(); // Must be manually synched by user!
        }

        public boolean add(E e) {
            synchronized (mutex) {return c.add(e);}
        }
        public boolean remove(Object o) {
            synchronized (mutex) {return c.remove(o);}
        }

        public boolean containsAll(Collection<?> coll) {
            synchronized (mutex) {return c.containsAll(coll);}
        }
        public boolean addAll(Collection<? extends E> coll) {
            synchronized (mutex) {return c.addAll(coll);}
        }
        public boolean removeAll(Collection<?> coll) {
            synchronized (mutex) {return c.removeAll(coll);}
        }
        public boolean retainAll(Collection<?> coll) {
            synchronized (mutex) {return c.retainAll(coll);}
        }
        public void clear() {
            synchronized (mutex) {c.clear();}
        }
        public String toString() {
            synchronized (mutex) {return c.toString();}
        }
        // Override default methods in Collection
        @Override
        public void forEach(Consumer<? super E> consumer) {
            synchronized (mutex) {c.forEach(consumer);}
        }
        @Override
        public boolean removeIf(Predicate<? super E> filter) {
            synchronized (mutex) {return c.removeIf(filter);}
        }
        @Override
        public Spliterator<E> spliterator() {
            return c.spliterator(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> stream() {
            return c.stream(); // Must be manually synched by user!
        }
        @Override
        public Stream<E> parallelStream() {
            return c.parallelStream(); // Must be manually synched by user!
        }
        private void writeObject(ObjectOutputStream s) throws IOException {
            synchronized (mutex) {s.defaultWriteObject();}
        }
    }

我们清楚的看到 mutex = this; 这个锁就是对象自己!
通过上面的源码我们可以知道了,synchronizedList实现线程安全的方法就是对自己暴力加锁,这效率能不低下吗?

在获取安全的list后遍历时,外层为何还要用synchronized同步?

官方文档就是如此使用synchronizedList的:

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized (list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

问题来了:既然封装类内部已经加了对象锁,为什么外部还要加一层对象锁?

先看下官方文档的解释吧:
# Understanding Collections and Thread Safety in Java

https://www.codejava.net/java-core/collections/understanding-collections-and-thread-safety-in-java

NOTE:
When using the iterator of a synchronized collection, we should use synchronized block to safeguard the iteration code because the iterator itself is not thread-safe. Consider the following code:

List<String> safeList = Collections.synchronizedList(new ArrayList<>());

// adds some elements to the list

Iterator<String> iterator = safeList.iterator();

while (iterator.hasNext()) {
    String next = iterator.next();
    System.out.println(next);
}

Although the safeList is thread-safe, its iterator is not, so we should manually add synchronized block like this:

synchronized (safeList) {
    while (iterator.hasNext()) {
        String next = iterator.next();
        System.out.println(next);
    }
}

其实,这个迭代的外部锁其实就是为了list.iterator()在读取过程中,不会本来hasNext()有的,但在调用i.next()的时候,另外一个线程把它删了,这个synchronized块是为了保障这三行代码在多个线程里同时执行的并发问题。

至于synchronizedList的内部锁,那是在并发执行add/remove的时候,不要把多个线程的东西加到list内部实现的同一个位置上去,导致数据丢失或者脏数据等问题,这是为了保证这个List在执行add/remove时不会存在并发问题。

简而言之,这两个锁是不同层面上的并发问题。

所以,当我们对synchronizedList进行遍历的时候一定不要忘了,在外部也加上synchronized(list),以保证线程安全。

— — — END — — —

运营人员:Monster




版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《说说线程安全包装:Collections.synchronizedList》的版权归原作者「ApeClub」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

关注ApeClub微信公众号

ApeClub微信公众号:apeclub

ApeClub

手机扫描上方二维码即可关注ApeClub微信公众号

ApeClub最新文章

精品公众号随机推荐

举报