volatile 和 synchronized 详解

一、背景

提起并发编程,我们不得不说起 volatile synchronized 这两个关键字,这两个关键字也是面试中常常被问到的,下面我们分别介绍一下这两个关键字以及二者的异同

首先我们先理解关于线程安全的两个名词:内存可见 和 执行顺序

内存可见:顾名思义就是线程执行结果在内存中对其它线程的可见性

执行顺序:控制代码的执行顺序及是否可以并发执行

二、volatile

volatile 解决的是内存可见性问题

2.1 volatile 原理

volatile原理是基于CPU内存屏障指令实现的

2.2 volatile 修饰的变量可见性

volatile是变量修饰符,其修饰的变量具有内存可见性

一般情况下线程在执行时,Java中为了加快程序的运行效率,会先把主存数据拷贝到线程本地(寄存器或是CPU缓存),操作完成后再把结果从线程本地缓存刷新到主存中,这样就会导致修改后放入变量结果同步到主存中需要一个过程,而此时另外的线程看到的还是修改之前的变量值,这样就会导致不一致

为了解决上述多线程中内存可见的问题,引入了 volatile 关键字,那么它为什么可以解决内存可见性问题呢?

答案: volatile 它会使得所有对 volatile 变量的读写都会直接读写主存,而不是先读写线程本地缓存,这样就保证了变量的内存可见性

2.3 volatile 禁止指令重排 

volatile可以禁止进行指令重排

指令重排: 处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。指令重排序不会影响单个线程的执行,但是会影响到线程并发执行时的正确性

线程执行到volatile修饰变量的读写操作时,其他线程对这个变量的操作肯定已经完成了,且结果已经同步到了主存中,即对其他的线程可见,本线程再对该变量操作完全没有问题的

2.4 volatile 使用范围

volatile关键字仅能实现对原始变量(如boolen、 short 、int 、long等)操作的原子性,不能保证复合操作的原子性,比如 i++

i++,实际上是由三个原子操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但不能保证i结果的正确性,原因如下:

比如有两个线程A和B对volatile修饰的i进行i++操作,i的初始值是0,A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了,A和B线程同步到主存中的i的值都是1

2.5 volatile 使用场景

1、 对变量的写入操作不依赖变量的当前值,或者只有单个线程更新变量的值

2、 该变量没有包含在具有其他变量的不变式中

三、synchronized

synchronized 既解决了内存可见性问题,又解决了执行顺序问题

synchronized 可以修饰代码块或方法,既可以保证可见性,又能够保证原子性

synchronized 锁具体实现过程:https://blog.csdn.net/ywlmsm1224811/article/details/108322059

3.1 synchronized 原理

synchronized 是基于 monitor 实现的

3.2 synchronized 修饰的代码块或方法保证内存可见性

通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中

3.3 synchronized 修饰的代码块或方法保证原子性

线程要么不执行(线程没有获取到对象锁),线程要么执行到底(线程获取到了对象锁),直到执行完释放锁

3.4 synchronized 使用范围

synchronized 不仅能修饰代码块,还可以修饰方法

3.5 synchronized 使用场景

需要控制多线程访问的方法或者更新的变量

四、volatile 和 synchronized 异同点

4.1 相同点

volatile 和 synchronized 都保证了内存可见性

4.2 不同点

1、volatile仅能使用在变量级别,synchronized则可以使用在变量、方法、和类级别的

2、volatile仅能实现变量的修改可见性,不能保证原子性,而synchronized则可以保证变量的修改可见性和原子性

3、volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞

4、volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化

5、由于 4 中的区别,在某些情况下 volatile 的性能优于 synchronized

五、总结

通过上面对 volatile 和 synchronized 的了解我们知道,volatile 是绝对不能替代 synchronized 的,最主要的原因就是 volatile 不能保证操作的原子性

参考:

https://blog.csdn.net/SEU_Calvin/article/details/52370068

https://blog.csdn.net/suifeng3051/article/details/52611233

https://blog.csdn.net/suifeng3051/article/details/52611310

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值