文档章节

深入理解Java内存模型 ——volatile

 王CHAO
发布于 2016/05/13 09:09
字数 2192
阅读 23
收藏 0

行业解决方案、产品招募中!想赚钱就来传!>>>

  • 欢迎关注我们的:

InfoQ - 促进软件开发领域知识与创新的传播

登录

966,690 四月 独立访问用户

全部话题

您目前处于: InfoQ首页 文章 深入理解Java内存模型(四)——volatile

深入理解Java内存模型(四)——volatile

作者 程晓明 发布于 2013年2月5日 | 注意:GMTC全球移动技术大会2016年6月24-25日,了解更多详情!62 讨论

volatile的特性

当我们声明共享变量为volatile后,对这个变量的读/写将会很特别。理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个读/写操作做了同步。下面我们通过具体的示例来说明,请看下面的示例代码:

class VolatileFeaturesExample {
    volatile long vl = 0L;  //使用volatile声明64位的long型变量

    public void set(long l) {
        vl = l;   //单个volatile变量的写
    }

    public void getAndIncrement () {
        vl++;    //复合(多个)volatile变量的读/写
    }


    public long get() {
        return vl;   //单个volatile变量的读
    }
}

假设有多个线程分别调用上面程序的三个方法,这个程序在语意上和下面程序等价:

class VolatileFeaturesExample {
    long vl = 0L;               // 64位的long型普通变量

    public synchronized void set(long l) {     //对单个的普通 变量的写用同一个监视器同步
        vl = l;
    }

    public void getAndIncrement () { //普通方法调用
        long temp = get();           //调用已同步的读方法
        temp += 1L;                  //普通写操作
        set(temp);                   //调用已同步的写方法
    }
    public synchronized long get() { 
    //对单个的普通变量的读用同一个监视器同步
        return vl;
    }
}

如上面示例程序所示,对一个volatile变量的单个读/写操作,与对一个普通变量的读/写操作使用同一个监视器锁来同步,它们之间的执行效果相同。

监视器锁的happens-before规则保证释放监视器和获取监视器的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。

相关厂商内容

一路走来技术人的创业故事

未来物联网中智能硬件的角色

人工智能的技术版图

GTLC全球技术领导力峰会7折优惠中!

传统车企为何对百度车联网oem青睐有加?14号,百度技术沙龙为你解答

相关赞助商

ArchSummit深圳2016将于7月15-16在华侨城洲际大酒店举行,现价8折抢购,团购报名更多优惠

监视器锁的语义决定了临界区代码的执行具有原子性。这意味着即使是64位的long型和double型变量,只要它是volatile变量,对该变量的读写就将具有原子性。如果是多个volatile操作或类似于volatile++这种复合操作,这些操作整体上不具有原子性。

简而言之,volatile变量自身具有下列特性:

  • 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
  • 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

volatile写-读建立的happens before关系

上面讲的是volatile变量自身的特性,对程序员来说,volatile对线程的内存可见性的影响比volatile自身的特性更为重要,也更需要我们去关注。

从JSR-133开始,volatile变量的写-读可以实现线程之间的通信。

从内存语义的角度来说,volatile与监视器锁有相同的效果:volatile写和监视器的释放有相同的内存语义;volatile读与监视器的获取有相同的内存语义。

请看下面使用volatile变量的示例代码:

class VolatileExample {
    int a = 0;
    volatile boolean flag = false;

    public void writer() {
        a = 1;                   //1
        flag = true;               //2
    }

    public void reader() {
        if (flag) {                //3
            int i =  a;           //4
            ……
        }
    }
}

假设线程A执行writer()方法之后,线程B执行reader()方法。根据happens before规则,这个过程建立的happens before 关系可以分为两类:

  1. 根据程序次序规则,1 happens before 2; 3 happens before 4。
  2. 根据volatile规则,2 happens before 3。
  3. 根据happens before 的传递性规则,1 happens before 4。

上述happens before 关系的图形化表现形式如下:

在上图中,每一个箭头链接的两个节点,代表了一个happens before 关系。黑色箭头表示程序顺序规则;橙色箭头表示volatile规则;蓝色箭头表示组合这些规则后提供的happens before保证。

这里A线程写一个volatile变量后,B线程读同一个volatile变量。A线程在写volatile变量之前所有可见的共享变量,在B线程读同一个volatile变量后,将立即变得对B线程可见。

volatile写-读的内存语义

volatile写的内存语义如下:

  • 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。

以上面示例程序VolatileExample为例,假设线程A首先执行writer()方法,随后线程B执行reader()方法,初始时两个线程的本地内存中的flag和a都是初始状态。下图是线程A执行volatile写后,共享变量的状态示意图:

如上图所示,线程A在写flag变量后,本地内存A中被线程A更新过的两个共享变量的值被刷新到主内存中。此时,本地内存A和主内存中的共享变量的值是一致的。

volatile读的内存语义如下:

  • 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

下面是线程B读同一个volatile变量后,共享变量的状态示意图:

如上图所示,在读flag变量后,本地内存B已经被置为无效。此时,线程B必须从主内存中读取共享变量。线程B的读取操作将导致本地内存B与主内存中的共享变量的值也变成一致的了。

如果�%9

粉丝 0
博文 5
码字总数 3246
作品 0
东城
私信 提问
加载中
请先登录后再评论。
Netty那点事(三)Channel与Pipeline

Channel是理解和使用Netty的核心。Channel的涉及内容较多,这里我使用由浅入深的介绍方法。在这篇文章中,我们主要介绍Channel部分中Pipeline实现机制。为了避免枯燥,借用一下《盗梦空间》的...

黄亿华
2013/11/24
2W
22
树莓派(Raspberry Pi):完美的家用服务器

自从树莓派发布后,所有在互联网上的网站为此激动人心的设备提供了很多有趣和具有挑战性的使用方法。虽然这些想法都很棒,但树莓派( RPi )最明显却又是最不吸引人的用处是:创建你的完美家用...

异次元
2013/11/09
5.6K
8
5分钟 maven3 快速入门指南

前提条件 你首先需要了解如何在电脑上安装软件。如果你不知道如何做到这一点,请询问你办公室,学校里的人,或花钱找人来解释这个给你。 不建议给Maven的服务邮箱来发邮件寻求支持。 安装Mav...

fanl1982
2014/01/23
1.2W
6
代码生成器--Codgen

Codgen是一个基于数据库元数据模型,使用freemarker模板引擎来构建输出的代码生成器。freemarker的数据模型结构通常来说都是一个Map树状结构模型,codgen也不例外,它的数据模型这棵树的根节...

黄天政
2013/01/29
1.4W
2
Javascript图元绘制库--ternlight

基于HTML CANVAS API的Javascript库,提供在HTML页面上绘制图元——如流程图的能力。 目前已支持简单的矩形图元和图元间的连线(直线、直角连线两种),拖拽图元等能力。 该javascript librar...

fancimage1
2013/02/07
6.2K
1

没有更多内容

加载失败,请刷新页面

加载更多

搞网站的你,不了解一下共享虚拟主机和备案问题

正文共:1474字 14图,预估阅读时间:4 分钟 今天分享的这一切要从域名备案说起。先科普一下,平时我们访问网站都是用域名访问的,通过DNS服务器将域名解析为IP地址(你知道上网时输入的URL...

郭松成
昨天
0
0
10 分钟学会 pillow 图像处理 16 式

PIL:Python Imaging Library,是Python环境下最受欢迎的图像处理库之一。 pillow简单优雅而功能强大,是图像相关机器学习任务中算法工程师的亲密合作伙伴。 我们将介绍pillow的如下16个图片...

zglg
昨天
0
0
3大排行榜告诉你,Java&Python有多稳

什么编程语言最受欢迎? 零基础小白学什么语言最好找工作? …… 关于这些问题的讨论从来都没停止 今天领扣🐱就来盘点一下 如今最受欢迎的语言到底是什么 Java&Python学习大礼包 资料领取方...

Lintcode
今天
2
0
这道原题答出来了还是跪!今年面试也太难了……

秋招已然到来,Amazon这不又发了一堆岗位,此时可以说是上岸最好机会!不过上周有同学反馈面试亚麻,遇到一题曾经刷过,惨的是最后还是跪了,班班仔细一问原来是这道。 给定一个整数序列,找...

九章算法
今天
0
0
【你只需看一次】YOLO 全系列目标检测算法

文章目录 一、概述 二、Yolo系列全家桶 YOLOv1 开山鼻祖之作 YOLOv2 YOLOv3 YOLOv4 目标检测tricks集大成者 YOLOv5 Fast YOLO Complex-YOLO MV-YOLO YOLO3D YOLO-6D YOLO-LITE Spiking-YOLO ......

osc_5p8bxoq2
13分钟前
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部