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;
        }
        
    }
}
原文地址:https://www.cnblogs.com/IvySue/p/7494060.html