您的位置:首页 > 编程语言 > Java开发

Java 并发——volatile

2018-11-21 17:48 651 查看


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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 架构师 程序员