常见并发

  1. 常见并发问题解决方式

    image.png

  2. 解释

    2.1 局部变量
    局部变量解决并发是因为局部变量仅仅存在于每个线程的工作内存中。同一个变量被不同的线程执行,会在各自的工作内存中创建副本。
    2.2 不可变对象
    不可变对象是一经创建,对外的形态不对改变的对象。例如字符串String=”hello”
    2.3 ThreadLocal
    ThreadLocal本质上是每一个线程有自己副本。每个线程互不影响。
    2.4 cas原子类(compareAndSwapInt)
    cas比较与置换。cas使用了三个基本操作数。内存地址V,旧的预期值A,要修改的新值B。只有当内存地址V所对应的值与旧值一样的,才可以将内存值A变为新值B。
    Atomic系统使用的是一种无锁化的CAS,基于乐观锁。
    CAS操作来对数据进行比较置换,如果操作失败了,会进入到while循环中,直到操作成功。cas
    方法是一个native方法,底层是通过c++来实现的,保证整个操作是原子性的,避免并发问题。
    2.5 synchoronized/ReentrantLock
    都是悲观锁策略

CopyOrWriteArrayList

  1. copyOrWriteArrayList等效不可变对象,运用不可变对象模式,使得集合在进行遍历操作的时候,不用加锁也能保证线程安全。<br /> 等效不可变对象的意思是,对象基本符合不可变对象的特征,但是某些情况下内部状态可能会改变。<br /> copyOrWriteArrayList内部维护一个array数据,只能通过getArraysetArray操作。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22489676/1629976362934-08557803-4e05-42b4-aef0-7784c8232388.png#clientId=u9f359c56-cccf-4&from=paste&height=566&id=u9b9e0ad7&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1131&originWidth=1618&originalType=binary&ratio=1&size=349512&status=done&style=none&taskId=u63f78f6e-a506-49a8-884f-05b86926374&width=809)<br /> 新增一个变量,通过getArray()获得array数组,然后新建一个数据,将原数组值放到新数组中,然后直接使用新的数组覆盖实例变量array。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/22489676/1629976614738-71fdf181-2bbb-4f18-a129-e753c3068bee.png#clientId=u9f359c56-cccf-4&from=paste&height=320&id=u4bd89048&margin=%5Bobject%20Object%5D&name=image.png&originHeight=640&originWidth=1266&originalType=binary&ratio=1&size=135997&status=done&style=none&taskId=u53b74e03-f343-4249-9a5c-5c15c1636c2&width=633)<br /> 其实是内部状态可能改变,但是存储状态不会改变,称之为等效不可变对象。<br /> 写时复制机制<br /> 写时复制是copyOrWriteArrayList的另一个特点。遍历是一个读操作,增加和删除都是用新数组覆盖旧数组值,是一个写操作。写时复制最终目的是为了在读多写少的场景。通过写时复制,让大量读请求在无需加锁牺牲性能的情况下保证并发读写情况下线程安全。<br /> ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22489676/1629977220530-d8a2c445-1bc5-4c56-9a07-974ac2c6b903.png#clientId=u9f359c56-cccf-4&from=paste&height=525&id=u52bcc349&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1050&originWidth=1657&originalType=binary&ratio=1&size=143866&status=done&style=none&taskId=u3a9374bc-6b47-4732-937e-92905a60188&width=828.5)<br /> 假设线程1读元素和线程2写元素同时进行,线程1读到的就可能是没有新增的元素,线程2新增是给array加锁,创建新array,最后新array覆盖旧array释放锁。这就是弱一致性。线程1读到的只是某一时刻的快照数据,无法读到最新数据。