zoukankan      html  css  js  c++  java
  • 【面试专栏】ArrayList 非线程安全案例并提供三种解决方案

    1. 复现问题

    import java.util.ArrayList;
    import java.util.List;
    import java.util.UUID;
    
    /**
     * 复现问题
     * 
     * @author CL
     *
     */
    public class RecurrenceProblem {
    
    	public static void main(String[] args) {
    		List<String> list = new ArrayList<String>();
    
    		// 启动30个线程
    		for (int i = 0; i < 30; i++) {
    			new Thread(() -> {
    				list.add(UUID.randomUUID().toString().substring(0, 8));
    				System.out.println(list);
    			}, String.valueOf(i)).start();
    		}
    	}
    
    }
    

      运行结果:

    [42251c59]
    [42251c59, 5839198b]
    [42251c59, 5839198b, 1283d17b]
    [42251c59, 5839198b, 1283d17b, 01fce852]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed, a8aa4fea]
    Exception in thread "21" [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed, a8aa4fea, 0d3b8cf8]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed, a8aa4fea, 0d3b8cf8, 6fb56487]
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed, a8aa4fea, 0d3b8cf8, 6fb56487, b9fb25b8]
    java.util.ConcurrentModificationException
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed, a8aa4fea, 0d3b8cf8, 6fb56487, b9fb25b8, 3b98b513]
    	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    	at java.util.ArrayList$Itr.next(ArrayList.java:851)
    	at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    	at java.lang.String.valueOf(String.java:2994)
    	at java.io.PrintStream.println(PrintStream.java:821)
    	at com.c3stones.demo.RecurrenceProblem.lambda$0(RecurrenceProblem.java:22)
    	at java.lang.Thread.run(Thread.java:745)
    [42251c59, 5839198b, 1283d17b, 01fce852, 857eb6d1, eb874846, 7cfa654c, 794c24d5, 68a415a7, f51c911a, 9f99c03a, 84213b11, b1d1c583, 04ecf6c3, 4d16693d, 65a4715e, e968aa23, b60db864, fb3a9828, 3fd4f004, 333308f6, 7143a717, 9ba40ac6, 06ae20ed, a8aa4fea, 0d3b8cf8, 6fb56487, b9fb25b8, 3b98b513, 83bd3f54]
    
    

      出现:java.util.ConcurrentModificationException异常

    2. 原因剖析

      java.util.ConcurrentModificationException即并发修改异常。
      查看ArrayList.add(...)源码:

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

      发现并没有对该方法加锁,因此在并发修改时,必然是线程不安全的,则抛出java.util.ConcurrentModificationException异常。

    3. 解决方案

    • Vector
        使用Vector代替ArrayList。
        查看Vector.add(...)源码:
    /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
    

      可以看到add方法中使用synchronized实现同步。代码修改为:

    import java.util.List;
    import java.util.UUID;
    import java.util.Vector;
    
    /**
     * 解决方案01-Vector
     * 
     * @author CL
     *
     */
    public class Solution01 {
    
    	public static void main(String[] args) {
    		List<String> list = new Vector<String>();
    
    		// 启动30个线程
    		for (int i = 0; i < 30; i++) {
    			new Thread(() -> {
    				list.add(UUID.randomUUID().toString().substring(0, 8));
    				System.out.println(list);
    			}, String.valueOf(i)).start();
    		}
    	}
    
    }
    

      运行结果:

    [759f9961]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda]
    [759f9961, ff871e85, 4a3939a2, 59e796cf]
    [759f9961, ff871e85, 4a3939a2]
    [759f9961, ff871e85]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf, 12155931]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf, 12155931, 12e61627]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf, 12155931, 12e61627, fa205c82]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf, 12155931, 12e61627, fa205c82, 04726e78]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf, 12155931, 12e61627, fa205c82, 04726e78, 0ee9c3d5]
    [759f9961, ff871e85, 4a3939a2, 59e796cf, cf4dfbda, bbc62489, 439941c6, 7a003645, fc224a2b, f52573e5, 1144e6c3, 4de11c35, ba2e2d82, 95d9f85e, d4cfdf23, 90dd544b, 59e75219, a3787684, fb5cf421, 9ca37cea, 8cbe36f6, 62494d83, 846001e0, ec1efeaf, 12155931, 12e61627, fa205c82, 04726e78, 0ee9c3d5, 097c1d4e]
    

      从运行结果可以看出,已经解决了问题。但是查看Vector类说明和ArrayList类说明:

    /*
     * @author  Lee Boynton
     * @author  Jonathan Payne
     * @see Collection
     * @see LinkedList
     * @since   JDK1.0
     */
    public class Vector<E>
        extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
    ...
    }
    
    
    /*
     * @author  Josh Bloch
     * @author  Neal Gafter
     * @see     Collection
     * @see     List
     * @see     LinkedList
     * @see     Vector
     * @since   1.2
     */
    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
    ...
    }
    

      可以看出,Vector是JDK1.0时出现的,ArrayList是JDK1.2出现的。因此,官方肯定不建议使用Vector来解决此问题。

    • Collections
        使用Collections工具类提供的方法来避免ArrayList出现的并发修改问题。代码修改为:
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.UUID;
    
    /**
     * 解决方案02-Collections
     * 
     * @author CL
     *
     */
    public class Solution02 {
    
    	public static void main(String[] args) {
    		List<String> list = Collections.synchronizedList(new ArrayList<String>());
    
    		// 启动30个线程
    		for (int i = 0; i < 30; i++) {
    			new Thread(() -> {
    				list.add(UUID.randomUUID().toString().substring(0, 8));
    				System.out.println(list);
    			}, String.valueOf(i)).start();
    		}
    	}
    
    }
    

      Set、Map在创建实例时也可以使用此方式:

    Set<String> set = Collections.synchronizedSet(new HashSet<String>());
    Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>());
    
    • CopyOnWrite
        CopyOnWrite写时复制。CopyOnWriteArrayList.add(...)方法:
    /**
     * 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();
        }
    }
    

      可以看出在方法进入时加锁。先创建一个比之前长度大1的数组,并在末尾添加元素,最后再将源容器指向新的容器。这样的好处是源容器不会添加任何元素,这也是一种读写分离的思想。
    代码修改为:

    import java.util.List;
    import java.util.UUID;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    /**
     * 解决方案03-CopyOnWrite
     * 
     * @author CL
     *
     */
    public class Solution03 {
    
    	public static void main(String[] args) {
    		List<String> list = new CopyOnWriteArrayList<String>();
    
    		// 启动30个线程
    		for (int i = 0; i < 30; i++) {
    			new Thread(() -> {
    				list.add(UUID.randomUUID().toString().substring(0, 8));
    				System.out.println(list);
    			}, String.valueOf(i)).start();
    		}
    	}
    
    }
    

      Set、Map在创建实例时也可以使用此方式:

    Set<String> set = new CopyOnWriteArraySet<String>();
    Map<Integer, String> map = new ConcurrentHashMap<Integer, String>();
    

    4. 项目地址

      collection-thread-unsafe-demo

  • 相关阅读:
    [CXF REST标准实战系列] 二、Spring4.0 整合 CXF3.0,实现测试接口
    [CXF REST标准实战系列] 一、JAXB xml与javaBean的转换
    项目中使用百度地图遇见的问题
    工作体会(第一次工作)
    第一家公司面试
    自我总结(九)---
    J2EE 第二阶段项目(八)
    J2EE 第二阶段项目之编写代码(六)
    J2EE 第二阶段项目之JUnit4进行单元测试(五)
    J2EE 第二阶段项目之编写代码(四)
  • 原文地址:https://www.cnblogs.com/cao-lei/p/13178329.html
Copyright © 2011-2022 走看看