集合框架一

197 阅读30分钟

欢迎大家关注 github.com/hsfxuebao/j… ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

4、集合框架

我们知道ArrayList是线程不安全,请编码一个不安全的案例并给出解决方案

4.1、ArrayList 不安全

我们知道ArrayList是线程不安全,请编码一个不安全的案例并给出解决方案

  • 代码

    public class ContainerNotSafeDemo {

    /*
     * 1 故障现象
     *   java.util.ConcurrentModificationException
     *
     * 2 导致原因
     *   并发争抢修改导致,参考我们的花名册签名情况。
     *   一个人正在写入,另一个同学过来抢夺,导致数据不一致异常。并发修改异常。
     *
     * */
    public static void main(String[] args) {
        listNotSafe();
    }
    
    private static void listNotSafe() {
        List<String> list=new ArrayList<>();
        // java.util.ConcurrentModificationException
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName() + "\t" + list);
            }, String.valueOf(i)).start();
        }
    }
    

    } 12345678910111213141516171819202122232425262728293031323334

  • 程序运行结果:由于 ArrayList 类的 add() 方法没有加锁,所以存在多线程并发安全问题

    java.util.ConcurrentModificationException at java.util.ArrayListItr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.checkForComodification(ArrayList.java:901) at java.util.ArrayListItr.next(ArrayList.java:851) at java.util.AbstractCollection.toString(AbstractCollection.java:461) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:131) at com.Heygo.ContainerNotSafeDemo.lambdalistNotSafelistNotSafe0(ContainerNotSafeDemo.java:26) at java.lang.Thread.run(Thread.java:748) 12345678

解决问题 ArrayList 线程不安全

  1. 使用 new Vector<>();(ArrayList所有方法加synchronized,太重)。
  2. 使用 Collections.synchronizedList(new ArrayList<>()); 转换成线程安全类。
  3. 使用 new java.concurrent.CopyOnWriteArrayList<>();(推荐)。

CopyOnWriteArrayList

CopyOnWriteArrayList 写时复制

  1. 写时复制:CopyOnWrite容器,即写时复制的容器。
  2. 往一个容器添加元素的时候,不直接往当前容器 Object[] 添加,而是先将当前 Object[] 进行Copy,复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后,再将原容器的引用指向新的容器setArray(newElements)
  3. 这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
  4. 所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。

CopyOnWriteArrayList 代码示例

  • 代码:使用 CopyOnWriteArrayList 集合类,保证 ArrayList 并发修改安全性的同时,也保证了并发读取的效率

    public class ContainerNotSafeDemo {

    /*
     * 1 故障现象
     *   java.util.ConcurrentModificationException
     *
     * 2 导致原因
     *   并发争抢修改导致,参考我们的花名册签名情况。
     *   一个人正在写入,另一个同学过来抢夺,导致数据不一致异常。并发修改异常。
     *
     * 3 解决方案
     *   3.1 new Vector<>();
     *   3.2 集合工具类:Collections.synchronizedList(new ArrayList<>());
     *   3.3 new CopyOnWriteArrayList<>()
     *       写时复制:CopyOnWrite容器即写时复制的容器。
     *       往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前object[]进行Copy,
     *       复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,
     *       添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);
     *       这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
     *       所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
     *
     * 4 优化建议(同样的错误不犯两次)
     *
     * */
    public static void main(String[] args) {
        listNotSafe();
    }
    
    private static void listNotSafe() {
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName() + "\t" + list);
            }, String.valueOf(i)).start();
        }
    }
    

    } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546

  • 程序运行结果

    5 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72] 8 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9] 10 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9] 2 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9] 3 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72] 12 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02] 7 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e] 4 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72] 15 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a] 14 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e] 13 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b] 1 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e] 22 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a] 11 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e] 6 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803] 9 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64] 27 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4, 1ce730cc, 5807293f, 8163070f] 26 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4, 1ce730cc, 5807293f] 25 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4, 1ce730cc] 24 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4] 23 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36] 21 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d] 20 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143] 19 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a] 18 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e] 17 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9] 16 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3] 30 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4, 1ce730cc, 5807293f, 8163070f, 8bc1cbfc, 58caaadd] 29 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4, 1ce730cc, 5807293f, 8163070f, 8bc1cbfc, 58caaadd, ce11ccb2] 28 [ce629c0f, 195253cd, 9d98dc22, b3ed3b72, 1542d43e, 1494f2e9, aa7e9f64, 413cf9d9, ce7e5748, 1feaa74e, 6d40a803, 5fa45f02, 86971d8b, 14886d8e, 232c6e3a, ecf34ff3, 9aa964b9, a4cf1f6e, 5427a83a, 250c0143, 4c32f82d, a76fe96a, e28f0c36, ee151ef4, 1ce730cc, 5807293f, 8163070f, 8bc1cbfc]

    12345678910111213141516171819202122232425262728293031

ArrayList 源码分析

  • 初始化时,构造了一个空的 Object[] 数组

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    
    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    

    12345678910111213

  • ArrayList 中使用 Object[] 数组存放数据

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
    

    1234567

  • 第一次添加元素时,初始化 Object[] 数组的大小为 DEFAULT_CAPACITY = 10

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;
    
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
    
        ensureExplicitCapacity(minCapacity);
    }
    

    123456789101112131415161718192021222324

  • 扩容操作

    • 每次扩容为旧容量的 1.5 倍

    • ArrayList 最大容量为 Integer.MAX_VALUE - 8

    • 使用 Arrays.copyOf()方法扩容,并将将原来数组中的值拷贝到新数组中

      private void ensureExplicitCapacity(int minCapacity) { modCount++;

      // overflow-conscious code
      if (minCapacity - elementData.length > 0)
          grow(minCapacity);
      

      }

      /**

      • The maximum size of array to allocate.
      • Some VMs reserve some header words in an array.
      • Attempts to allocate larger arrays may result in
      • OutOfMemoryError: Requested array size exceeds VM limit */ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

      /**

      • Increases the capacity to ensure that it can hold at least the
      • number of elements specified by the minimum capacity argument.
      • @param minCapacity the desired minimum capacity */ private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } 123456789101112131415161718192021222324252627282930313233

Collections. synchronizedList() 源码

  • Collections.synchronizedList() 方法:由于 ArrayList 实现了 RandomAccess 接口,所以在方法内部创建了一个 SynchronizedRandomAccessList 的实例

    /**
     * Returns a synchronized (thread-safe) list backed by the specified
     * list.  In order to guarantee serial access, it is critical that
     * <strong>all</strong> access to the backing list is accomplished
     * through the returned list.<p>
     *
     * It is imperative that the user manually synchronize on the returned
     * list when iterating over it:
     * <pre>
     *  List list = Collections.synchronizedList(new ArrayList());
     *      ...
     *  synchronized (list) {
     *      Iterator i = list.iterator(); // Must be in synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * </pre>
     * Failure to follow this advice may result in non-deterministic behavior.
     *
     * <p>The returned list will be serializable if the specified list is
     * serializable.
     *
     * @param  <T> the class of the objects in the list
     * @param  list the list to be "wrapped" in a synchronized list.
     * @return a synchronized view of the specified list.
     */
    public static <T> List<T> synchronizedList(List<T> list) {
        return (list instanceof RandomAccess ?
                new SynchronizedRandomAccessList<>(list) :
                new SynchronizedList<>(list));
    }
    

    12345678910111213141516171819202122232425262728293031

  • SynchronizedRandomAccessList 类是 Collections 类的静态内部类

  • SynchronizedRandomAccessList 的父类为 SynchronizedList 类

  • super(list); 表示调用父类 SynchronizedList 的构造方法

    static class SynchronizedRandomAccessList extends SynchronizedList implements RandomAccess {

    SynchronizedRandomAccessList(List<E> list) {
        super(list);
    }
    

    1234567

  • SynchronizedList 类也是 Collections 类的静态内部类

  • SynchronizedList 类的父类是 SynchronizedCollection 类

  • super(list); 表示调用父类 SynchronizedCollection 的构造方法

  • SynchronizedList 内部维护了 ArrayList 的引用:this.list = list;

    static class SynchronizedList extends SynchronizedCollection implements List { private static final long serialVersionUID = -7754090372962971524L;

    final List<E> list;
    
    SynchronizedList(List<E> list) {
        super(list);
        this.list = list;
    }
    

    1234567891011

  • SynchronizedCollection 类也是 Collections 类的静态内部类

  • 在 SynchronizedCollection 内部维护了 ArrayList 的引用:this.c = Objects.requireNonNull(c);

  • 通过 final Object mutex 这把锁,给集合中的所有方法都加上锁,保证多线程并发的安全性

    static class SynchronizedCollection implements Collection, 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();}
    }
    

    } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586

CopyOnWriteArrayList 源码分析

  • CopyOnWriteArrayList 内部维护了两个重要成员变量:

    • ReentrantLock 锁:保证多线程并发修改的安全性
    • Object[] array 数组:使用 volatile 修饰,保证内存的可见性

    public class CopyOnWriteArrayList implements List, RandomAccess, Cloneable, java.io.Serializable { private static final long serialVersionUID = 8673264195747942595L;

    /** The lock protecting all mutators */
    final transient ReentrantLock lock = new ReentrantLock();
    
    /** The array, accessed only via getArray/setArray. */
    private transient volatile Object[] array;
    
    /**
     * Gets the array.  Non-private so as to also be accessible
     * from CopyOnWriteArraySet class.
     */
    final Object[] getArray() {
        return array;
    }
    
    /**
     * Sets the array.
     */
    final void setArray(Object[] a) {
        array = a;
    }
    
    /**
     * Creates an empty list.
     */
    public CopyOnWriteArrayList() {
        setArray(new Object[0]);
    }
    

    12345678910111213141516171819202122232425262728293031

  • 添加元素:

    • 上锁{

    • 数组长度扩容 1 个元素,将旧元素拷贝至新数组与中,将新元素放在数组末尾

    • 修改 rivate transient volatile Object[] array; 的引用

    • }解锁

      /**

      • Appends the specified element to the end of this list.
      • @param e element to be appended to this list
      • @return {@code true} (as specified by {@link Collection#add}) */ public boolean add(E e) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] elements = getArray(); int len = elements.length; Object[] newElements = Arrays.copyOf(elements, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } } 1234567891011121314151617181920
  • 由于采用了读写分离,所以读取集合无需加锁,提高了读的并发性

    public String toString() { return Arrays.toString(getArray()); } 123

4.2、HashSet 不安全

演示 HashSet 线程不安全

  • 代码

    public class ContainerNotSafeDemo { public static void main(String[] args) { setNoSafe(); }

    private static void setNoSafe() {
        Set<String> set=new HashSet<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(Thread.currentThread().getName() + "\t" + set);
            }, String.valueOf(i)).start();
        }
    }
    

    } 12345678910111213141516171819202122

  • 程序运行结果:java.util.ConcurrentModificationException

    java.util.ConcurrentModificationException at java.util.HashMapHashIterator.nextNode(HashMap.java:1437)atjava.util.HashMapHashIterator.nextNode(HashMap.java:1437) at java.util.HashMapKeyIterator.next(HashMap.java:1461) at java.util.AbstractCollection.toString(AbstractCollection.java:461) at java.lang.String.valueOf(String.java:2994) at java.lang.StringBuilder.append(StringBuilder.java:131) at com.Heygo.ContainerNotSafeDemo.lambdasetNoSafesetNoSafe2(ContainerNotSafeDemo.java:71) at java.lang.Thread.run(Thread.java:748) 12345678

解决 HashSet 线程不安全问题

  1. 使用 CollectionssynchronizedSet() 方法将 HashSet 转为线程安全版本
  2. 使用 CopyOnWriteArraySet 类:读写分离

CopyOnWriteArraySet 代码示例

  • 代码

    /**

    • @ClassName ContainerNotSafeDemo

    • @Description TODO

    • @Author Heygo

    • @Date 2020/8/7 21:35

    • @Version 1.0 */ public class ContainerNotSafeDemo { public static void main(String[] args) { setNoSafe(); }

      private static void setNoSafe() { Set set = new CopyOnWriteArraySet<>(); for (int i = 1; i <= 30; i++) { new Thread(() -> { set.add(UUID.randomUUID().toString().substring(0, 8)); System.out.println(Thread.currentThread().getName() + "\t" + set); }, String.valueOf(i)).start(); } } } 12345678910111213141516171819202122

  • 程序运行结果

    1 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb] 7 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a] 16 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177] 18 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e] 15 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a] 21 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e] 4 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7] 3 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7] 2 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7] 13 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7] 24 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d] 8 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9] 14 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9] 30 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d, 85e01c3e, 0edf78f9, a0c65913] 5 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d] 11 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d] 9 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb] 12 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24] 26 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d, 85e01c3e, 0edf78f9, a0c65913, 9ae99682, 84b58b16] 25 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d, 85e01c3e, 0edf78f9, a0c65913, 9ae99682] 29 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d, 85e01c3e, 0edf78f9, a0c65913, 9ae99682] 28 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d, 85e01c3e, 0edf78f9] 27 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698, 22177284, 6252e23d, 85e01c3e] 22 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22, 866bc698] 23 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7, 3f6ded3e, f995ef22] 20 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89, 73bd78e7] 19 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6, 23fbb23e, ec60cb89] 17 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a, 25b9d177, e5567ae6] 6 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb] 10 [d9f8ebbc, 1d6205eb, 836575a9, ce40bd87, 7bacd0f6, 57d5347f, e1676b1c, c10c5256, 247ff963, 3a5b3feb, ce846b2d, 9e050a24, 0e7e56e9, bf76ebc7, 6072428a] 123456789101112131415161718192021222324252627282930

HashSet 源码分析

  • HashSet 的构造器:底层维护了一个负载因子为 0.75 的 HashMap

    public class HashSet extends AbstractSet implements Set, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;
    
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    
    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }
    

    123456789101112131415161718

  • add() 方法:

    • key 为待添加的元素

    • value 统一为 private static final Object PRESENT = new Object();

      /**

      • Adds the specified element to this set if it is not already present.
      • More formally, adds the specified element e to this set if
      • this set contains no element e2 such that
      • (e==null ? e2==null : e.equals(e2)).
      • If this set already contains the element, the call leaves the set
      • unchanged and returns false.
      • @param e element to be added to this set
      • @return true if this set did not already contain the specified
      • element */ public boolean add(E e) { return map.put(e, PRESENT)==null; } 123456789101112131415

Collections.synchronizedSet() 源码分析

  • Collections.synchronizedSet() 方法创建了一个 synchronizedSet 类的实例

    /**
     * Returns a synchronized (thread-safe) set backed by the specified
     * set.  In order to guarantee serial access, it is critical that
     * <strong>all</strong> access to the backing set is accomplished
     * through the returned set.<p>
     *
     * It is imperative that the user manually synchronize on the returned
     * set when iterating over it:
     * <pre>
     *  Set s = Collections.synchronizedSet(new HashSet());
     *      ...
     *  synchronized (s) {
     *      Iterator i = s.iterator(); // Must be in the synchronized block
     *      while (i.hasNext())
     *          foo(i.next());
     *  }
     * </pre>
     * Failure to follow this advice may result in non-deterministic behavior.
     *
     * <p>The returned set will be serializable if the specified set is
     * serializable.
     *
     * @param  <T> the class of the objects in the set
     * @param  s the set to be "wrapped" in a synchronized set.
     * @return a synchronized view of the specified set.
     */
    public static <T> Set<T> synchronizedSet(Set<T> s) {
        return new SynchronizedSet<>(s);
    }
    

    1234567891011121314151617181920212223242526272829

  • SynchronizedSet 类是 Collections 的静态内部类

  • SynchronizedSet 类的父类是 SynchronizedCollection 类

  • super(s); 调用父类构造器

    static class SynchronizedSet extends SynchronizedCollection implements Set { private static final long serialVersionUID = 487447009682186044L;

    SynchronizedSet(Set<E> s) {
        super(s);
    }
    

    12345678

  • SynchronizedSet 和 SynchronizedList 都继承自 SynchronizedCollection 类,均是通过 mutex 这把锁解决了多线程并发修改的安全问题

    static class SynchronizedCollection implements Collection, 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();}
    }
    

    } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586

CopyOnWriteArraySet 源码分析

  • CopyOnWriteArraySet 内部维护了一个 CopyOnWriteArrayList 实例,典型的挂羊皮卖狗肉

    public class CopyOnWriteArraySet extends AbstractSet implements java.io.Serializable { private static final long serialVersionUID = 5457747651344034263L;

    private final CopyOnWriteArrayList<E> al;
    
    /**
     * Creates an empty set.
     */
    public CopyOnWriteArraySet() {
        al = new CopyOnWriteArrayList<E>();
    }
    

    123456789101112

  • copyOnWriteArraySet.add() 方法:调用 copyOnWriteArrayList.addIfAbsent() 方法

    // CopyOnWriteArraySet 类的 add() 方法
    /**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element {@code e} to this set if
     * the set contains no element {@code e2} such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns {@code false}.
     *
     * @param e element to be added to this set
     * @return {@code true} if this set did not already contain the specified
     *         element
     */
    public boolean add(E e) {
        return al.addIfAbsent(e);
    }
    

    12345678910111213141516

  • copyOnWriteArrayList.addIfAbsent() 方法:

    • 获取 Object[] 数组的快照

    • 将元素添加至 ArrayList(这里看不太懂)

      /**

      • Appends the element, if not present.
      • @param e element to be added to this list, if absent
      • @return {@code true} if the element was added */ public boolean addIfAbsent(E e) { Object[] snapshot = getArray(); return indexOf(e, snapshot, 0, snapshot.length) >= 0 ? false : addIfAbsent(e, snapshot); }

      /**

      • A version of addIfAbsent using the strong hint that given
      • recent snapshot does not contain e. */ private boolean addIfAbsent(E e, Object[] snapshot) { final ReentrantLock lock = this.lock; lock.lock(); try { Object[] current = getArray(); int len = current.length; if (snapshot != current) { // Optimize for lost race to another addXXX operation int common = Math.min(snapshot.length, len); for (int i = 0; i < common; i++) if (current[i] != snapshot[i] && eq(e, current[i])) return false; if (indexOf(e, current, common, len) >= 0) return false; } Object[] newElements = Arrays.copyOf(current, len + 1); newElements[len] = e; setArray(newElements); return true; } finally { lock.unlock(); } }