关于不安全的集合类与解决替代方案1-ArrayList

1、多线程异常案例

ArrayList底层是维护了一个数组。

ArrayList线程不安全,因为其add、remove等方法没有synchronized关键字修饰,也没有任何同步加锁处理。

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!扩容一
        elementData[size++] = e;//add方法默认放在最后一个
        return true;
    }

番外:注意现在的空参初始化ArrayList初始容量不是10了,是0,在第一次add的时候会将扩容为10。具体的ArrayList扩容机制可以参考大佬文章,作者:烟雨星空

https://blog.csdn.net/qq_26542493/article/details/88873168

ArrayList线程不安全的案例

public class UnsafeJH {

    public static void main(String[] args){
        //ArrayList线程不安全
        List<String> list = new ArrayList<>();

        for(int i = 1;i<=30;i++){//30个线程执行add操作
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

运行结果:

[null, f93845ca, 4011b4a3, 34fefde8, d2c0c25d]
Exception in thread "4" Exception in thread "10" Exception in thread "20" Exception in thread "27" Exception in thread "29" 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 UnsafeJH.lambda$main$0(UnsafeJH.java:20)
	at java.lang.Thread.run(Thread.java:748)
java.util.ConcurrentModificationException

 java.util.ConcurrentModificationException异常(并发修改异常)

2、解决方案

2.1、Vector

//add方法  
public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }

//get方法
 public synchronized E get(int index) {
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);

        return elementData(index);
    }

Vector中的大多数方法都用synchronized关键字修饰,可以解决线程安全问题,但是因为同意时间只能有一个线程访问资源,所以效率会很慢。

2.2、Collections.synchronizedList(list)

通过Collections工具类将一个线程不安全的ArrayList封装成为一个线程安全的。

Collections.synchronizedList(List list) 返回的是一个 SynchronizedList 的对象,这个对象以组合的方式将对 List 的接口方法操作,委托给传入的 list 对象,并且对所有的接口方法对象加锁,得到并发安全性。底层原理可以参考https://blog.csdn.net/weixin_38575051/article/details/94000044作者ToSimpleL

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

2.3、写时复制 CopyOnWriteArrayList

读写分离的思想,当写操作的时候将,不直接往容器Object[]添加,而是先将当前容器Object[]进行Copy,生成一个新的Object[] newElements ;先往newElements 中添加要加的元素,再将原容器的引用指向新的容器;这样做的好处是可以对CopyOnWriteArrayList容器进行并发的读操作,而不需要加锁。从而提高效率。

List<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();

   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);//复制list,扩容长度+1
            newElements[len] = e;//给复制后的list加元素
            setArray(newElements);//写回list
            return true;
        } finally {
            lock.unlock();//释放锁
        }
    }

get()源码不用加锁

 /**
     * {@inheritDoc}
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    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、付费专栏及课程。

余额充值