可见性
一个线程对主存中的一个变量进行修改,另一个线程不可见,因为其是从缓存中读取值的,缓存中的值是开始运行时从主存中读取的未修改前的值
解决办法:volatile,加了volatile的变量每次都需要从主存中读取,它可以用来修饰成员变量和静态成员变量,线程操作volatile变量都是直接操作主存
synchronized也可以保证可见性
volatile保证不了原子性
有序性
JVM会在不影响正确性的前提下,调整语句执行顺序,这种特性称为指令重排,单线程下没有影响,多线程下会影响正确性
加了volatile的变量其之前的代码不会进行重排序
Volatile原理
Volatile底层实现原理是内存屏障
对volatile变量的写指令后会加写屏障
写屏障,保证在写屏障之前对变量的改动都会同步到主存中
保证写屏障之前的代码不会重排到写屏障后面
对volatile变量的读指令前会加读屏障
读屏障,保证在读屏障之后对变量的读取,加载的都是主存中最新的数据
保证读屏障之后的代码不会重排到写屏障前面
当变量完全被synchronized保护时,才可以保证变量的有序性
问题1
加了volatile将在写操作之后加上写屏障,写屏障之前的代码不会重排到写屏障之后。
这里3如果不加volatile,可能会指令重排,赋值操作在构造方法之前执行,导致1得到的是不完整的变量
问题2
只需要第一次进入锁内运行其中代码,后面直接返回INSTANCE即可
问题3
不加空判断,当线程一进入锁内之后因为还没有创建对象,线程二通过1将进入阻塞状态,等线程一运行完后线程二因为没经过判断,又初始化了一个对象
CAS(compareAndSet)
CAS必须借助volatile才能读取到共享变量的最新值实现 比较并交换 的效果
为什么无锁效率高?
无锁情况下,即使重试失败,线程始终高速运行,没有等待,而synchronized会让线程在没有获得锁的时候发生上下文切换,进入阻塞。
注意:如没有额外CPU支持,相当于还是会发生上下文切换
CAS 乐观锁
Synchronized 悲观锁
原子整数
AtomicBoolean
AtomicInteger
AtomicLong
AtomicInteger i = new AtomicInteger(x); // 将x的值赋值给i
increamentAndGet(); //++x
getAndIncreament(); //x++
i.getAndAdd(5) // x,x+5
i.addAndGet(5) //x+5,x+5
// 读取到的值 设置值
i.updateAndGet(x->x10) //x=x10
原子引用
AtomicReference
AtomicStampedReference
只要其它线程动过了共享变量,如果自己的cas就算失败,这时,仅比较值是不够的,需要再加一个版本号
需要新加两个参数,版本号以及对版本号的修改,如果版本号一致,则返回修改值
AtomicMarkableReference
有时不关心引用变量更改了几次,只关心是否更改过,所以就有了AtomicMarkableReference
原子数组
保护数组中的元素值
supplier 提供者 ()->结果 无参数
function 函数 一个参数一个结果 (参数)->结果 BiFunction(参数1,参数2)->结果
consumer 消费者 一个参数没结果 (参数)->void BiConsumer(参数1,参数2)->
字段更新器
利用字段更新器,可以对对象的某个Field进行原子操作,只能配合volatile修饰的字段使用,否则·出现异常
AtomicReferenceFieldUpdate
AtomicIntegerFieldUpdate
AtomicLongFieldUpdate
原子累加器
原子累加器能够提升性能,原因
在有竞争时,设置多个累加单元,Therad-0 累加 Cell[0],而 Thread-1 累加Cell[1]… 最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量,因此减少了CAS 重试失败,从而提高性能。
longAdder
longAdder的三个关键域,使用increament()方法进行自增
cas锁
CAS锁
不可用于生产实践
CellsBusy类似cas锁,0表示未加锁,1表示加锁
Cell累加单元类
Value用来保存累加的值
此注解的作用用来防治缓存行的伪共享
缓存行伪共享
CPU与内存的速度差异很大,需要靠预读数据至缓存来提升效率
缓存以缓存行为单位,每个缓存行对应着一块内存,一般为64byte
缓存的加入会造成数据副本的产生,同一份数据会缓存在不同核心的缓存行中
CPU为保存数据一致性,如果某个CPU核心更改了数据,其它CPU核心对应的整个缓存行失效
@sun.misc.Contended注解的作用
使用此注解的对象或字段的前后各增加128字节大小的padding,从而让CPU将对象预读至缓存时不用占用不同的缓存行,不会造成对方缓存行为的失效,防止一个缓存行有多个cell对象
Add()
累加主要调用了add()方法
longAccumulate()
sum 方法
将cells数组赋值给as,将设定的基础值base赋值给sum。判断数组是否为空,不为空则将数组中元素拿出来判断是否为空,不为空进行累加,最后返回累加值
Unsafe
获取unsfe对象
unsafe cas相关方法