线程安全一一原子操作

in 笔记 with 0 comment

一个线程安全问题的栗子

image.png

出现问题的原因

用javap查看编译结果

image.png

实质计算的过程就是压栈出栈的过程

image.png

根本问题所在是A线程还未写入主内存,B线程就读取到主内存的老数据

image.png

什么是原子操作

原子操作可以是一个步骤,也可以是多个步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分(不可中断性)。
简单来说就是:整个操作视为一个整体,资源在该次操作中保持一致,操作期间不能有操作介入。

解决方案

方案一:用synchronized

image.png

方案二:用lock

image.png

方案三:用AtomicInteger

image.png

AtomicInteger原理一一CAS

CAS:即Comparea and swap

image.png

image.png

image.png

AtomicInteger就是用CAS+自旋(循环),自旋不是CAS的一部分,CAS原理就是compare and swap比较替换,自旋是CAS使用的一种思想。
这里A线程用CAS方式去读取主内存数据后进行压栈计算,如果期间B线程也用CAS方式去读取就会不断自旋等A线程把新值刷新到主内存后B线程再进去压栈计算。

CAS的底层实现

通过对象的引用和再内存地址中的偏移量。具体实现方法如unsafe.compareAndSwapInt()

image.png

image.png

CAS就是提供原子操作的方式,JDK还提供了很多原子操作封装类

其他CAS原子操作类

image.png

AtomicIntegerArray的原理不是数组中每个元素都是AtomicInteger,而是普通的int数组。只是取出来的时候再进行CAS操作。

image.png

AtomicIntegerFieldUpdate是对某一个对象中int字段进行CAS操作。

image.png

AtomicReference原理是通过CAS来创建对象。

image.png

Adder的原理就是increment的累加sum。Accumlator可以自定义累加规则,类似stream中的reduce。

image.png

高并发的CAS可能会导致CPU性能消耗特别大,CPU飙升。所以1.8引入了Accumulator和Adder,思想是分而治之。

CAS的三个问题

1. CPU高负荷问题

循环+CAS,自旋的实现会让所有线程处于高频运行,争抢CPU执行时间的状态。如果长时间不成功会带来很大的CPU资源消耗。

2. 仅能对单个变量操作

仅针对单个变量的操作,不能对于多个变量来实现原子操作。

3. ABA问题

ABA问题

image.png

image.png

image

一个T1线程进行CAS比较的时候发现是相等的值,但此时的值已经不是以前的值了,而是被其他线程覆盖后再次变成T1线程比较相等的值了,此时T1线程执行更新就是无效的。可以通过使用带版本号的原子操作来解决。

带版本号的CAS

AtomicStampedReference就是带版本号的CAS操作类。
其实现原理是CAS时加上版本号,如:top.compareAndSet(oldTop, newTop, v, v+1)