ArrayList在多并发下的不安全问题以及解决方法

ArrayList在多并发下的不安全问题以及解决方法

为啥不安全

多线程并发插入元素和读取元素的时候会出现 ConcurrentModificationException

 public static void main(String[] args) {
       
        ArrayList<Integer> l2  = new ArrayList<>();

        for (int i = 0; i < 50; i++) {
            final int tmp = i;
            new Thread(() -> {
                l2.add(tmp);
                System.out.println(l2);
                
            }).start();
        }
    }

出现的结果

Exception in thread "Thread-11" Exception in thread "Thread-14" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	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 testCopyOnWrite.ListTest.lambda$main$0(ListTest.java:23)
	at java.lang.Thread.run(Thread.java:748)

高并发的情况下list的插入元素和读取元素出现了并发异常

add方法

   public boolean add(E e) {
        // list的长度加1
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 放入元素 
       elementData[size++] = e;
        return true;
    }

  public void add(int index, E element) {
        rangeCheckForAdd(index);
        
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

get方法

  public E get(int index) {
        // 检查index是否符合规范
        rangeCheck(index);
        return elementData(index);
    }
1. 使用Vector()数组来解决这个问题
Vector<Integer> v = new Vector<>();

Vector()继承的类

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

他的add方法

	// 使用synchronized加锁操作
	public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }	

	public void add(int index, E element) {
        // 
        insertElementAt(element, index);
    }
    
    // 
    public synchronized void insertElementAt(E obj, int index) {
        modCount++;
        if (index > elementCount) {
            throw new ArrayIndexOutOfBoundsException(index
                                                     + " > " + elementCount);
        }
        ensureCapacityHelper(elementCount + 1);
        System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
        elementData[index] = obj;
        elementCount++;
    }
    
  • 他的add方法都是使用了synchronized关键字,进行了加锁操作,只有代码块执行完毕之后才可以才会释放锁,其他操作才可以对Vector()数组进行操作
   public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }
  • get方法和add方法都是进行synchronized的加锁,并且加锁的是同一对象。
2. 使用Connections集合
 		ArrayList<Integer> l2  = new ArrayList<>();
        List<Integer> list = Collections.synchronizedList(l2)

SynchronizedList继承的类

static class SynchronizedList<E>
        extends SynchronizedCollection<E>
        implements List<E> 

get方法和add方法

		public void add(int index, E element) {
            synchronized (mutex) {list.add(index, element);}
        }
        public E get(int index) {
            synchronized (mutex) {return list.get(index);}
        }
  • 加锁对象是mutex

    final Object mutex;     // Object on which to synchronize
    

    在创建这个SynchronizedCollection的时候就创建的final对象mutex

3. 使用 CopyOnWriteArrayList

名字如此 复制后再写的List

List<Integer> l1 = new CopyOnWriteArrayList<>();

CopyOnWriteArrayList继承的类和实现的接口

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

add

使用Lock锁来实现我们的add方法 并且在插入的时候,会先获取到原来list中的数,然后添加到数后面,最后覆盖掉原来的list

  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);
            // 添加e
            newElements[len] = e;
            // 覆盖
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

   // 这个也是同理
   public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

get方法

 	private E get(Object[] a, int index) {
        return (E) a[index];
    }

   
    public E get(int index) {
        return get(getArray(), index);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值