volatile的用法

1.volatile 关键字解释

volatile是一个特征修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

2.内存模型的相关概念

计算机在执行程序时,每条指令都是在 CPU 中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟 CPU 执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在 CPU 里面就有了高速缓存。也就是,当程序在运行过程中,会将运算需要的数据从主存复制一份到 CPU 的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。举个简单的例子,比如下面的这段代码:

i=i+1;

当单线程执行的时候 不会出问题。假设i的初始值是0。假如两个线程同时执行呢?得到的答案会不会是2呢?很明显是不确定的。

极端的情况:AB两个线程,A线程读取到i的值是0 读进自己的高速缓存中 进行+1,还没有来得及把i的值刷新到主存中,这时B线程刚好去主内存中读取i的值,这时i的值依旧是0;所以AB两个线程执行完 导致i的值不是2而是1。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。

3.并发编程中的三个要素

  • 原子性: 一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。
  • 有序性: 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序)
  • 可见性: 一个线程对共享变量的修改,另一个线程能够立刻看到。
3.1.原子性

一个很经典的例子就是银行账户转账问题
比如从账户 A 向账户 B 转 1000 元,那么必然包括 2 个操作:从账户 A 减去 1000 元,往账户 B 加上 1000 元。
试想一下,如果这 2 个操作不具备原子性,会造成什么样的后果。
假如从账户 A 减去 1000 元之后,操作突然中止。然后又从 B 取出了 500 元,取出 500 元之后,再执行往账户 B 加上 1000 元 的操作。
这样就会导致账户 A虽然减去了 1000 元,但是账户 B 没有收到这个转过来的 1000 元。
所以这 2 个操作必须要具备原子性才能保证不出现一些意外的问题。

3.2.有序性

导致有序性的原因是编译优化
我们都知道处理器为了拥有更好的运算效率,会自动优化、排序执行我们写的代码,但会确保执行结果不变。比如下面这段代码:

int a = 0; // 语句 1
int b = 0; // 语句 2
i  ; // 语句 3
b  ; // 语句 4

我们认为计算机是按1 2 3 4 这个顺序执行的,可实际上并不是。
因为 1 2没有数据依赖关系 ,3 4 没有数据依赖关系。所以计算机很有可能 2 1 3 4,1 2 4 3 等这样执行。
单线程下是没有问题。多线程下就不能保证不出问题了。

3.3.可见性

对于可见性,Java提供了 volatile 关键字来保证可见性。
当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过 synchronized 和 Lock 也能够保证可见性,synchronized 和 Lock 能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

4.volatile关键字的两层语义

一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后,那么就具备了两层语义:

  • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
  • 禁止进行指令重排序。

但是volatile不能保证原子性。采用synchronized,加锁或者使用原子操作类(AtomicInteger)才可以保证原子性。
不过volatile一定程度上可以保证有序性。

5.volatile使用场景

synchronized 关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而 volatile 关键字在某些情况下性能要优于 synchronized,但是要注意 volatile 关键字是无法替代 synchronized 关键字的,因为 volatile 关键字无法保证操作的原子性。
通常来说,使用 volatile 必须具备以下 2 个条件:

  • 对变量的写操作不依赖于当前值
  • 该变量没有包含在具有其他变量的不变式中
  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值