前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >内存模型与轻量级同步机制volatile

内存模型与轻量级同步机制volatile

作者头像
搬砖俱乐部
发布于 2019-06-15 09:33:33
发布于 2019-06-15 09:33:33
50300
代码可运行
举报
文章被收录于专栏:BanzClubBanzClub
运行总次数:0
代码可运行

Java中为了线程通信的安全性(数据一致性),除了提供内置锁synchronized和显示锁ReentrantLock,还提供了另外一种线程同步机制——volatile,是一种轻量级同步机制。不过,通常很难轻易的理解volatile的真正意义。下面通过一个例子来认识一下volatile(摘自《深入理解Java虚拟机》):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public volatile static int race = 0;

private static void increase() {
    race++;
}

private static final int THREADS_COUNT = 20;

public static void main(String[] args) {
    Thread[] threads = new Thread[THREADS_COUNT];
    for (int i = 0; i < THREADS_COUNT; i++) {
        threads[i] = new Thread(() -> {
            for (int i1 = 0; i1 < 10000; i1++) {
                increase();
            }
        });
        threads[i].start();
    }
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(race);
}

通过运行这个例子,我们发现race变量最后的值,肯定会小于200000,这是为什么呢?由于volatile只能保证可见性,不能保证原子性。而race++不是原子操作,所以需要在increase方法加锁来保证原子性。而在increase方法加锁后,即使race不用volatile修饰也能得到期望值。看完之后还是一头雾水,没有真正理解其中的意义。

下面我们通过Java内存模型分析,来真正理解volatile的含义以及使用场景:

CPU缓存一致性

我们知道现代计算机为了解决存储设备与处理器处理速度的差距,在CPU和主存之间增加了多层高速缓存,每个CPU都会有一套高速缓存,那么多核计算机就有多套高速缓存。这些高速缓存与内存之间进行读写访问的过程,在不同处理器有不一样的实现方式,也就是不同处理器的内存模型是不一致的(图a,Intel的共享L2缓存;图b,AMD的独享L2缓存)。

当程序运行过程中,会将运算所需要的数据从主存中复制一份到高速缓存中,各个处理器都操作对应的高速缓存的数据,而不直接操作主存,当运算结束后,将高速缓存中的最新数据刷到主存中。这样可以极大的提升CPU的吞吐量,但也引入了新的问题,也就是缓存一致性问题。Intel的MESI协议的提出,就是为了保证每个缓存中使用的共享数据都是一致的,它的具体思想是,每个缓存(Cache)不仅知道自己的读写操作,而且也监听其他Cache的读写操作。当进行读操作时,Cache可以从主存中或者其他Cache中读取数据;当进行写数据时,不仅将Cache的数据反向更新到主存中,也要通知其他Cache该数据无效,需要其他Cache重新从主存中读取数据。

Java内存模型

Java虚拟机也有自己的Java内存模型(Java Memory Model JMM),用来屏蔽掉各种硬件和操作系统的内存访问差异,以实现各种平台下Java内存访问效果的一致。Java内存模型规定了所有共享数据的存储都在Java虚拟机的主内存(堆)中,每个线程拥有自己的工作内存(栈),用来保存被该线程使用到的数据的主内存副本。线程对数据的操作都必须在工作内存中进行,而不能直接操作主内存,也不能直接读写其他线程工作内存的数据,线程间的数据传递都需要通过主内存来完成。

Java内存模型的特性
原子性

Java内存模型定义了8种原子性的操作,来保证数据在主内存和工作内存中的交互,包括:锁定(lock)、解锁(unlock)、读取(read)、载入(load)、使用(use)、赋值(assign)、存储(store)。

在Java语言中,对基本数据类型的变量的读取赋值操作也是原子性的(long、double的非原子性协定),对引用类型变量的读取和赋值操作也是原子性的,但对于一些++、x=y这种运算赋值不是原子性的,也就是多个原子性操作组合在一起就不是原子性的操作了。Java还提供了更大范围的原子性操作,通过synchronized和显式锁ReentrantLock来实现。

可见性

可见性是指当一个线程修改了共享数据的值时,其他线程能理解得知这个变化。volatile能保证新值能立即同步到主内存中,普通变量不能保证立即,所以普通变量不能保证可见性。另外同步机制synchronized与显式锁ReentrantLock也能保证可见性,因为每次只允许一个线程操作共享数据,只有等当前线程操作完,其他线程才能获得权限,所以同步机制也能保证可见性。

有序性

Java线程的天然有序性可以一句话来总结:如果在本线程内观察,所有的操作都是有序的,如果在一个线程中观察另一个线程,所有操作都是无序的。由于Java编译器可以对Java代码进行指令重排序,也就是单线程下,指令重排序不会影响结果,但多线程情况下,会影响执行结果的。Java内存模型通过happens-before原则,来推导两个操作的执行顺序,否则编译器将无法保证有序性。所以为了保证代码执行的有序性,Java提供volatile和内置锁synchronized来完成。

重新认识volatile

特点一:volatile保证可见性

特点二:volatile禁止指令重排序

volatile的使用场景

再看开始的例子,volatile修饰的race只能保证可见性和顺序性,不能保证race这个非原子性操作的原子性,所以不能得到期望值200000。

在并发编程中,volatile只是对共享数据的一种优化方式,并不能代替synchronized,只有在一些特殊的情景下,才能使用它:

  • 对变量的写操作不依赖于当前值
  • 变量不需要与其他的状态变量共同参与不便约束

所以volatile很适合做某些状态值,如用volatile修饰初始化标识,加载一些初始化配置信息后,其他线程可以继续初始化的后续工作;还可以用volatile可以当做一些通知变量和数据,比如一些只能通过设置来配置的共享数据,其他线程不会修改,只会依赖其值的变化,做特定的操作。


  1. 《Java高并发编程详解-多线程与架构设计》
  2. 《Java并发编程艺术》
  3. 《深入理解Java虚拟机》
  4. https://blog.csdn.net/muxiqingyang/article/details/6615199
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BanzClub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
八股文常客——Java内存模型JMM
“高效并发”是本书讲解Java虚拟机的最后一个部分,将会向读者介绍虚拟机如何实现多线程、多线程之间由于共享和竞争数据而导致的一系列问题及解决方案。
燃192
2023/02/28
3260
八股文常客——Java内存模型JMM
死磕并发:Java内存模型
首先我们在了解java内存模型之前先看一下计算机内存模型,理解了计算机内存模型的话后面在看JMM就会简单的多,上篇文章我是直接写的。
乱敲代码
2019/09/17
4590
死磕并发:Java内存模型
深入理解JVM(③)学习Java的内存模型
Java内存模型(Java Memory Model)用来屏蔽各种硬件和操作系统的内存访问差异,这使得Java能够变得非常灵活而不用考虑各系统间的兼容性等问题。定义Java内存模型并非一件容易的事情,从Java出生开始经过长时间的验证和修补,直至JDK5发布后Java内存模型才终于成熟、完善起来了。
纪莫
2020/07/10
3940
Java并发:volatile关键字详解
volatile关键字可以说是Java虚拟机提供的最轻量级的同步机制,但是它并不容易完全被正确、完整地理解,以至于许多程序员都习惯不去使用它,遇到需要处理多线程数据竞争问题的时候一律使用synchronized来进行同步。了解volatile变量的语义对了解多线程操作的其他特性很有意义,在本文中我们将介绍volatile的语义到底是什么。由于volatile关键字与Java内存模型(Java Memory Model,JMM)有较多的关联,因此在介绍volatile关键字前我们会先介绍下Java内存模型。
Java架构师必看
2021/05/18
6310
Java并发:volatile关键字详解
Java程序员面试必备:Volatile全方位解析
volatile是Java程序员必备的基础,也是面试官非常喜欢问的一个话题,本文跟大家一起开启volatile学习之旅,如果有不正确的地方,也麻烦大家指出哈,一起相互学习~
捡田螺的小男孩
2020/08/13
5980
Java程序员面试必备:Volatile全方位解析
轻松理解计算机的内存模型及Java内存模型
本文转载自:https://www.hollischuang.com/archives/2550
aoho求索
2019/06/17
1.5K0
轻松理解计算机的内存模型及Java内存模型
你真的知道Java内存模型是什么吗
前几天,发了一篇文章,介绍了一下JVM内存结构、Java内存模型以及Java对象模型之间的区别。有很多小伙伴反馈希望可以深入的讲解下每个知识点。Java内存模型,是这三个知识点当中最晦涩难懂的一个,而且涉及到很多背景知识和相关知识。
格姗知识圈
2019/07/19
6980
你真的知道Java内存模型是什么吗
Java内存模型分析
计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。
用户2141593
2019/02/20
3910
Java内存模型分析
计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。
矿泉水
2018/05/11
6611
Java内存模型分析
高并发编程-重新认识Java内存模型(JMM)
高并发编程-通过volatile重新认识CPU缓存 和 Java内存模型(JMM)
小小工匠
2021/08/17
3470
Java中Volatile关键字详解
可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的,通俗点说,就是一个线程修改了变量值另一个线程能看到修改结果。在java中volatile,synchronized和final能实现可见性。
用户5640963
2019/07/26
5311
Java内存模型
多任务处理在现代计算机操作系统中几乎已经是一项必备的功能了。计算机cpu的运算速度与它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O、网络通信或数据库访问上。如果不希望处理器在大部分时间里都处于等待其他资源的状态,那么并发的处理多项任务是最容易想到、也是非常有效的“压榨”处理器运算能力的一种手段。 服务端是java语言最擅长的领域之一。如果写好并发应用程序是服务端程序开发的难点之一,java语言和虚拟机提供了许多工具来帮助程序员降低门槛,并且各种中间件服务器、各类框架都努力的替程序员处理更多的并发希捷,使得程序员在编码过程中更关注业务逻辑。但无论语言、中间件和框架多么先进,都不能独立的完成所有并发处理的事情,所以了解并发的内幕也是一个高级程序员不可缺少的课程。 高效并发是本教程的最后一部分,主要讲解虚拟机如何实现多线程、多线程之间由于共享和竞争数据而导致的一系列问题及解决方案。
栋先生
2018/09/29
8230
Java内存模型
Java内存模型
Java 内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
Java架构师必看
2021/07/14
3480
走进高并发(三)深入理解Java内存模型
在讨论Java内存模型之前,这里先一起聊聊CPU、高速缓存以及主内存,在了解这些知识后,对理解Java内存模型会有很大的帮助。
itlemon
2020/06/23
4100
你真的懂volatile关键字吗
volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。
Bug开发工程师
2018/07/23
6450
你真的懂volatile关键字吗
Java内存模型
在多线程处理问题和提高计算机处理效率之间的因果关系看似是理所当然的,但是其中还存在一定差距。计算机在处理问题时不止是在计算,还存在着大量的内存交互IO操作,而这两个之间存在着几个数量级之间的差距,所以现代计算机系统为了弥补这种差距,都设计了多级高速缓存。而这又引入了一个新的问题,内存一致性。
Mr.Shelby
2024/12/22
850
Java内存模型
快速掌握并发编程---深入了解volatile
今天聊得这个volatile是一个轻量级的synchronized,它在多线程开发中保证了共享变量的“可见性”。
田维常
2020/11/03
6120
快速掌握并发编程---深入了解volatile
Java内存模型
在多核时代,如何提高CPU的性能成为了一个永恒的话题,而这个话题的讨论主要就是如何定义一个高性能的内存模型,内存模型用于定义处理器的各层缓存与共享内存的同步机制及线程和内存交互的规则。 Java的世界也有属于它自己的内存模型,Java内存模型,即Java Memory Model,简称JMM。由于Java被定义成一种跨平台的语言,所以在内存的描述上面也要能是跨平台的,Java虚拟机试图定义一种统一的内存模型,能将各种底层硬件及操作系统的内存访问差异进行封装,使Java程序在不同硬件及操作系统上都能达到相同的
用户1263954
2018/01/30
9930
Java内存模型
原 【JDK并发基础】Java内存模型详解
       无论你是Java还是C,或者其他编程语言编写高并发程序时,都或多或少的会涉及内存模型。高并发程序下数据访问的一致性和安全性受到挑战,为了保证程序正确执行,Java内存模型(以下简称JMM)由此而诞生。如果不理解JMM,就会对内存可见性,有序性等问题出现时无从下手。本文将从以下几个方面进行JMM的说明:        1.内存模型的相关概念        2.可见性        3.有序性        4.原子性 1.内存模型的相关概念 1.1 Java虚拟机运行时数据区        在
我叫刘半仙
2018/04/16
9950
原  【JDK并发基础】Java内存模型详解
java中的内存模型
现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。
互扯程序
2018/07/30
6340
java中的内存模型
相关推荐
八股文常客——Java内存模型JMM
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验