关于ArrayList线程不安全那点事

ArrayList线程不安全

可能看过面试的小伙伴都知道ArrayList非线程安全,那来个demo 看看如何的线程不安全。
安排:

源码异常

public class ArrayTest {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        List<String> list=new ArrayList<String>();
        for (int i = 1; i <=3; i++)
        {
    
    
            new Thread(() -> {
    
    
                list.add(UUID.randomUUID().toString().substring(0,8));
                //System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

执行结果:
如果不加System.out.println(list); 不会报错,但是我们也不知道到底list集合中到底是啥。
很神奇的是我们加上 System.out.println(list); 就有了如下的执行结果:

第一次运行:
在这里插入图片描述
第二次运行:
在这里插入图片描述
第三次运行:
在这里插入图片描述
但看这个结果就很难受,每次结果都不一样,就连List中的size都不一样,这就是多线程编程的不确定性,我们需要的是把这种不确定性变成确定性。

在这里插入图片描述
这种机制叫做,集合的快速失败机制 “fail-fast”。
那么为啥会产生这种机制呢?
一句话总结:
打印内容的时候,内部自己在循环遍历这个List,会自动生成一个iterator 来遍历该 list,但同时该 list 正在被 Iterator.remove() 修改。Java 一般不允许一个线程在遍历 Collection 时另一个线程修改它。
查看源码:
在这里插入图片描述
在这里插入图片描述
遍历过程中,要检测是否modCount是否等于期待的值,如果不是抛出异常。

产生异常的原因

通俗的来讲就是线程争夺资源类,出现异常。
专业:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

源码剖析

先稍等,我们先来看下整体的Collection的大家族的UM图
在这里插入图片描述
我们在学习基础的时候,知道Vector是线程安全的,那么他为什么是安全的呢?因为他有锁,转到源码去看看。
在这里插入图片描述
我们可以看到在jdk1.2开始的时候,Vector就已经在add方法上加上了同步锁。
这也就解释了为什么,collection家族中 有的是线程安全有的是非线程安全。在常规的web中普通的增删改查不需要那么高的及时性与并发性。使用锁反而会影响效率,因此ArrayList更多的用于常见的web开发中,对于高并发中要求及时性比较强则使用线程安全类的集合。

在这里插入图片描述
源码也很简单:
在这里插入图片描述
使用了读写分离 的策略:
大意:就是将原来的元素复制到了一个新数组中,且长度应该加1,然后在新数组末尾加上要添加的元素,最后设置新数组为自己的array。
但是我们看到了他的追加源码,就一定认为他读取也是安全的么,接下来看get的源码:
在这里插入图片描述

这个方法没有加锁因此是线程不安全的,因为这个分成了两步,分别是获取数组和获取元素,。假设当前线程在获取数组(执行getArray())后,其他线程修改了这个CopyOnWriteArrayList,那么它里面的元素就会改变,但此时当前线程返回的仍然是旧的数组,所以返回的元素就不是最新的了,这就是写时复制策略产生的弱一致性问题。
因此 写时复制能够保证写入时候的一致性,但是在读取的时候,一致性并不强。

解决方案

常见的线程安全的集合:

  1. 转为线程安全Collections.synchronizedList(list),同理synchronizedMap将Map转为线程安全。
  2. Vector
  3. CopyOnWriteArrayList

猜你喜欢

转载自blog.csdn.net/abc8125/article/details/109155248