zoukankan      html  css  js  c++  java
  • Collections.synchronizedList()

    以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;
            }
            
        }
    }
  • 相关阅读:
    centos 新增用户, 然后他在主目录添加网站403Forbbiden
    linux 把用户加入一个组&从这个组中移除
    [THINKPHP] 温故知新之getFieldBy
    php 获取指定月份的开始结束时间
    apache 占用内存总量与每个apache进程的平均内存占用量计算
    网站并发300就很慢
    centos定时备份数据库超简单示例
    php导出excel时间错误(同一个时间戳,用date得到不同的时间)
    设置iframe 载入页面的效果跟直接打开这个页面一样
    node基础09:第2个node web服务器
  • 原文地址:https://www.cnblogs.com/IvySue/p/7494060.html
Copyright © 2011-2022 走看看