【JUC】并发编程(三)

可见性

一个线程对主存中的一个变量进行修改,另一个线程不可见,因为其是从缓存中读取值的,缓存中的值是开始运行时从主存中读取的未修改前的值

解决办法: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相关方法
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值