Java 并发——volatile
JVM内存管理
概述
volatile 是轻量级的 synchronized。volatile 作用于共享变量,具备了“锁”的特性,这是为了确保共享变量能被准确和一致性地更新,这是 volatile 的可见性。同时,它也阉割了 scnchronized 的一写功能,比如:原子性。
内存模型
图如篇首。
首先我们应该明白CPU是执行命令的场所,当需要处理数据时,CPU会从主内存(计算机内存)中取值,这样很慢。后来有了CPU高速缓存(cache),也就是CPU上有一小块存储空间缓存了从主内存中获取的数据,CPU直接读取cache,效率大增。
然后,问题出现了,待处理的数据并非是主内存中的原型,而是一个副本,在多线程场景下,这个副本的处理结果很有可能会失控。这个问题也就是缓存一致性问题,即cache和主内存数据同步问题。目前我知道解决缓存一致性问题有两种方案:
通过在总线加LOCK锁
通过缓存一致性协议
第一种是通过独占CPU方式实现,同一时间只有一个CPU在运行,效率低下。
第二种允许多核处理,并且让共享副本在线程之间具有可见性。
可见性
通过 volatile 实现了缓存一致性,其工作原理如下:
当某个CPU在写数据时,如果发现操作的变量是共享变量,则会通知其他CPU告知该变量的缓存行是无效的,因此其他CPU在读取该变量时,发现其无效会重新从主存中加载数据。
volatile 的可见性只针对当CPU从主内存中加载共享变量的时候。但是当线程A、B同时加载了共享变量i,后者说线程A先加载了i,在A将i写入之前,B加载了i,B加载的i仍然是主内存中i的初始值。
非原子性
线程内存与主内存的交互过程如下:
主要有以下5项操作:
read:从主内存中读取变量
load:复制变量到线程本地内存作为副本
use:运算副本
assign:给副本赋值
store:副本写入线程本地内存
write:线程内存中的共享副本刷新主内存中的共享变量
上一章节——“可见性”中已经声明 volatile 的可见性只针对当CPU从主内存中加载共享变量的时候,即load之前,一旦load,无论主内存中的共享变量发生了什么,副本的值不会被主内存同步。也就是说,volatile不具有原子性。
网友解答:
volatile的非原子性:线程工作内容中的值从主内存中直接加载,一旦加载完成,就不会再产生对应的变化。JVM保证的是从主内存中加载到线程工作内存中的值是最新的,但是无法保证原子性。 volatile解决的是变量读时的可见性问题,无法保证原子性。
[blockquote] [p]package com.zhoupq.multiThread.Volatile;public class VolatileDemo implements Runnable
{
static volatile int i = 1;[url=mailto:br/>@Override@Override
- 10031---Java并发编程:volatile关键字解析
- Java并发编程:Java内存模型和volatile
- Java并发volatile理解
- Java并发编程:volatile关键字解析
- 【Java并发编程】之十五:并发编程中实现内存可见的两种方法比较:加锁和volatile变量
- 【Java并发编程】10、Java 理论与实践: 正确使用 Volatile 变量
- Java并发编程之volatile解析
- Java 并发:volatile 关键字解析
- 【1】Java 并发编程--深入分析Volatile的实现原理
- java高并发设计(七)--volatile的使用
- java并发编程之volatile
- java 并发之volatile
- Java并发(3)- 聊聊Volatile
- Java并发编程:volatile关键字解析
- java并发之volatile
- 深入分析Java并发中volatile的实现原理
- Java并发:volatile内存可见性和指令重排
- 【Java】高并发同步Volatile的使用
- Java并发编程:volatile关键字解析
- java用volatile或AtomicBoolean实现高效并发处理 (只初始化一次的功能要求)