Java多线程相关知识【10】--解释volatile关键字

# Java多线程相关知识【10】--解释volatile关键字

菜鸟的一个学习笔记,大神请绕路。

## 1.问题的引入

​ 假设两个人同时对银行的一个账户进行存入1元钱操作,若银行未进行一定的保障,则有可能造成两个人同时存入一元钱,而这个账户仅仅会添加一元钱,而将会丢失一元钱的情况,那么如何保证这种情况不被发生呢?

## 2.问题发生的原因

​ 由于两人同时存入前,计算机读取数据时,并不会判断在读取数据后是否会发成改变,并且都将相应读到的数据存到计算机内,而仅对当前读到的数据进行保存,而忽略了多个人同时操作时发生的与时间有关的问题,故会造成读到脏数据的情况。(详见操作系统,多线程相关原理的解释)

## 3.解决的方法

### 3.1.解决方法的理论

### 3.1.1总线加锁

​ 总线加锁即当操作共享数据时,将会为访问的数据进行全部加锁的方式,这种方式虽然能解决相关程序的运行问题,但是,同样也会造成系统的运行效率低下等问题。

### 3.1.2.使用cpu高速缓存一致性协议(Intel 提出)

思想:cpu写入数据时,发现被共享时,会通知其他拥有副本的cpu需要立即更新缓存,即之前的信息失效,其他的cpu在收到信号后,会重新到相应内存区重新获取相应数据,这也就解决多个线程访问时数据不一致的问题。而Java在使用volatile关键字时,即使用了这个设计的思想。

### 3.2.Java中的多线程的三大原则

#### 3.2.1.原子性(atomic)

​ 一个操作要么都成功,要么都失败,不能因为任何原因被中断。

#### 3.2.2.有序性(order)

相应的操作需要按顺序执行(重排序为只要求结果一致性(重排序能优化单线程的执行效率),但在多线程中,会产生相关的错误)

##### Java默认提供的有序性(happens-before)

1. 代码执行时顺序为写在前面的代码先发生,写在后面的后发生。

2. 解锁操作必须在锁之后。

3. 多线程时,volatile修饰的关键字先执行写操作,后执行读操作。

4. 如果A操作先于B操作,B操作先于C操作,那么A操作必定先于C操作。

5. 在线程启动时,线程的创建必定优先于线程的执行。

6. 在线程中断时,线程的中断操作(interrupt)必须发生在捕获异常发生之前。

7. 对象的初始化,必须发生在销毁之前。

8. 线程的所有操作必须发生在线程的销毁之前。

#### 3.3.3.可见性(visable)

任何时间看到的数据应为最新的数据,而并非为旧的数据(防止操作系统的与时间有关的错误)

### 3.3.Java中的解决方法(使用volatile)

在Java中,未了解决操作系统中的与时间有关的错误的问题时,为了简单起见,通常会使用volatile关键字,但本关键字也有一定的使用限制,具体如下:

#### 3.3.1.使用volatile关键字的优点和缺点

1. 一旦此变量被关键字修饰,则虚拟机则保证了执行的可见性(V)和有序性(O),但并不能保证这个变量的原子性(A)。

2. 保证重排序时,指令的执行顺序不被修改

3. 强制使缓存的数据立刻写入主存

4. 若对关键字修饰的变量进行写操作,则会是其他cpu的缓存的数据在写入修改后立即失效。

#### 3.3.2.volatile关键字的使用场景

1. 需要进行状态量的标记时,可使用本关键字。

2. 需要保证关键字修饰的语句的执行的先后顺序不发送改变,即运行一致性时,可使用本关键字。







0 个评论

要回复文章请先登录注册