美文网首页
ArrayList线程不安全举例及解决

ArrayList线程不安全举例及解决

作者: 陈二狗想吃肉 | 来源:发表于2021-05-19 15:48 被阅读0次

    ArrayList部分源码

    public class ArrayList<E> extends AbstractList<E>

            implements List<E>, RandomAccess, Cloneable, java.io.Serializable

    {

        /**

        * 列表元素集合数组

        * 如果新建ArrayList对象时没有指定大小,那么会将EMPTY_ELEMENTDATA赋值给elementData,

        * 并在第一次添加元素时,将列表容量设置为DEFAULT_CAPACITY

        */

        transient Object[] elementData;

        /**

        * 列表大小,elementData中存储的元素个数

        */

        private int size;

    }

    add方法

    public boolean add(E e) {

        /**

        * 添加一个元素时,做了如下两步操作

        * 1.判断列表的capacity容量是否足够,是否需要扩容

        * 2.真正将元素放在列表的元素数组里面

        */

        ensureCapacityInternal(size + 1);  // Increments modCount!!

        elementData[size++] = e;

        return true;

    }

    ArrayList线程不安全的表现

    在多个线程进行add操作时可能会导致elementData数组越界。

    public static void main(String[] args) throws InterruptedException {

            final List<Integer> list = new ArrayList<Integer>();

            // 线程A将0-1000添加到list

            new Thread(() -> {

                for (int i = 0; i < 1000 ; i++) {

                    list.add(i);

                    try {

                        Thread.sleep(1);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }).start();

            // 线程B将1000-2000添加到列表

            new Thread(() -> {

                for (int i = 1000; i < 2000 ; i++) {

                    list.add(i);

                    try {

                        Thread.sleep(1);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

            }).start();

            Thread.sleep(1000);

            // 打印所有结果

            for (int i = 0; i < list.size(); i++) {

                System.out.println("第" + (i + 1) + "个元素为:" + list.get(i));

            }

    }

    多线程情况下,一个线程正在写入,另一个线程也在写入,导致数据不一致异常,并发生修改异常 java.util.ConcurrentModificationException

    public static void main(String[] args) throws InterruptedException {

            List<String> list = new ArrayList<>();

            for (int i =1; i<=30 ; i++) {

                new Thread(() -> {

                    list.add("a");

                    list.add("b");

                    list.add("c");

                    list.add("d");

                    System.out.println(list.toString());

                }).start();

            }

    }

    解决办法

    //Synchronized对代码进行加锁,力度大,所以代码执行效率低下

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

    //写时复制通过lock机制进行枷锁

    /*CopyOnWrite容器即写时复制的容器。往一个容器添加元索的时候,不直接往当前容器Object[]添加,

    而是先将当前容器Object[]进行Copy,复制出一个新的容器object[] newElements,

    然后往新的容器object[] newElements 里添加元素,

    添加完元素之后,再将原容器的引用指向新的容器setArray(newElements);。

    这样做的好处是可以CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。

    所以CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器*/

    List<String> list = new CopyOnWriteArrayList();

    List<String> list = new Vector<>();

    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();

        }

    }

    额外说下 ArrayList与LinkedList;这两个都是接口List下的一个实现,用法都一样,但用的场所的有点不同,ArrayList适合于进行大量的随机访问的情况下使用,LinkedList适合在表中进行插入、删除时使用,二者都是非线程安全,解决方法同上(为了避免线程安全,以上采取的方法,特别是第二种,其实是非常损耗性能的)

    ————————————————

    相关文章

      网友评论

          本文标题:ArrayList线程不安全举例及解决

          本文链接:https://www.haomeiwen.com/subject/yutpjltx.html