多线程之Volatile与CAS原理

多线程之Volatile与CAS原理

1、Volatile

vilatile有两个作用:

  1. 保证线程可见性
  2. 禁止指令重排序

可见性
会将堆内存中的值copy一份到自己的工作线程中,如果要改,那么先改工作内存中的值,再改堆内存中的值,但是另外一个线程读这个变量的时候,读的是没有更改的时候的值,什么时候读新的值,不好控制。加了voliate,一个线程堆变量的改变,另外一个线程可以看到。

可见性的本质
是使用了CPU的缓存一致性协议MESI。由于不同的线程运行到不同CPU上,所以不同的CPU也需要缓存,用的是CPU的缓存一致性协才能保证这个值的可见。

有序性
和CPU有关系,将指令并发的执行,这种架构需要编译器将指令重新排序。 有个例子,DCL单例模式。

有序性本质
底层原理是加了读屏障与写屏障loadfence,storefance。

单例模式可以分为三种形式,饿汉式、懒汉式以及DCL双重检查。
首先是饿汉式的:
在这里插入图片描述

懒汉式的:
在这里插入图片描述
但是懒汉式的线程不安全,两个线程会发现同时进入了if后面,同时new了新的instance,可以加锁解决,但是要确保锁的粒度小一些,这就是DCL。
在这里插入图片描述
需要加个volatile,不加会出现指令重排序。因为new的时候,指令分为3步,第一步为申请内存(值为默认值),第二步给成员变量初始化,第三步将内存的内容赋值给instance。
如果指令重排序,那么会instance指向了第一步的默认值。(超高级并发才能出现这种情况)

在这里插入图片描述

2、CAS

cas又称无锁优化、自旋锁。

最经典的Atomicinteger,使用CAS表示线程安全的类。

CAS原理: 内部调用unsafe类的compareandset方法。
CAS有三个参数,1.要改的值V 2.期望当前的值E 3.设定的新值 CAS是CPU原语的支持,是指令级别,不可被打断。所以在判断V == E时,不可能又另外的线程去改。
如果往hotspot的C源码看下,会发现其实CAS底层原语仍旧是用lock cxg命令的,其实也是从原语层面上了锁。

CAS有个问题:
ABA问题,1变成2,又变成了1,对外不可见。怎么解决?加个版本号,修改时版本号+1,加version,所以还需要检查version。在java中式用AtomicStampedRefence实现的。如果引用那么会有问题,是int的话没有问题。
Unsafe就像C 或者C++的指针,能释放内存也可以分配内存。(可以直接操作内存)

自增的时候,有三种方法保证一致性,一种synchronized,一种是atomicinteger,还有一种是longadder。

LongAdder用分段锁概念,atomicAdd用的无锁,sync最慢。一般longadder最快。
1000个线程,250个线程锁再第一块,另外250个锁再第二块。整体相加就得出(但是蛇和线程特别多的情况,如果少了则没有优势)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值