文档章节

JMM内存模型(一)&volatile关键字的可见性

字数 2267
阅读 155
收藏 0

在说这个之前,我想先说一下计算机的内存模型:

1.计算机内存模型

CPU在执行的时候,肯定要有数据,而数据在内存中放着呢,这里的内存就是计算机的物理内存,刚开始还好,但是随着技术的发展,CPU处理的速度越来越快,而从内存中读取和写入数据的过程和CPU的执行速度比起来差距就会越来越大,所说设计师,就在物理内存与CPU之间,加入了缓存的概念:也就是CPU在运行的时候,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。

(说到这里,你应该能想到在高并发,使用多线程处理的时候,存在的问题。)

单核CPU只含有一套L1,L2,L3缓存;如果CPU含有多个核心,即多核CPU,则每个核心都含有一套L1(甚至和L2)缓存,而共享L3(或者和L2)缓存。

当你的计算式是:

单核CPU,单线程。 核心的缓存只被一个线程访问。缓存独占,不会出现访问冲突等问题。

单核CPU,多线程。 进程中的多个线程会同时访问进程中的共享数据,CPU将某块内存加载到缓存后,不同线程在访问相同的物理地址的时候,都会映射到相同的缓存位置,这样即使发生线程的切换,缓存仍然不会失效。但由于任何时刻只能有一个线程在执行,因此不会出现缓存访问冲突。

多核CPU,多线程。 每个核都至少有一个L1 缓存。多个线程访问进程中的某个共享内存,且这多个线程分别在不同的核心上执行,则每个核心都会在各自的caehe中保留一份共享内存的缓冲。由于多核是可以并行的,可能会出现多个线程同时写各自的缓存的情况,而各自的cache之间的数据就有可能不同。

2.JMM内存模型

JMM全称为Java Memory Model  java内存模型,它只是一组规范,并不真实存在,(看好了,只是一组规范、一个定义),它描述的是一个规则。通过这组规范定义程序中的变量的访问方式。以此来解决多核CPU多线程时造成的问题(请想一想为什么会是多核CPU多线程时)。

JMM规定了工作内存、主内存,而主内存是共享区域,所有线程都可以访问,工作内存是每个线程的工作内存,每个线程对变量的操作必须在自己的工作内存中进行。在运行时将变量从主内存中拷贝到工作内存中,对变量进行操作,操作完后再将变量写会主内存,不能直接在主内存中操作变量。再说一遍:这是规范,是java定义的一组程序运行时的规范。看到这,是不是发现与计算机的内存模型特别像

3.JAVA内存区域

在这里再说一下java的内存区域:堆、栈、方法区、本地方法区、程序计数器

  1. 方法区:线程共享区域,主要用于存储虚拟机加载的类信息、常量、静态变量等数据。

  2. 堆:线程共享区域,虚拟机启动时创建,主要用于存放对象的实例,所以也是java垃圾回收最频繁的一个区域

  3. 栈:线程私有区域,与线程同时创建,栈数量与线程数量相等,以栈帧定义,执行每个方法时,都会创建一个栈帧存储方法的信息:操作数栈、动态链接方法、返回值、返回地址等信息,每个方法执行从调用到结束,对应着这个栈帧的入栈出栈。

  4. 程序计数器、本地方法栈我们先不关心,有心者请自行学习。

如果你看到这,我觉得你应该会疑惑,说JMM的时候,会什么要把计算机的内存模型、JAVA的内存区域都描述一下,稍后你就会知道。

在这里我还要再强调一遍,jmm内存模型的主内存和工作内存与java内存区域的堆、栈、方法区等不是同一个层次的,无法类比。一个是规范、规则,就相当于校规似的。如果真要对应,那就如同你想的  栈---工作内存,堆、方法区---主内存。

现在我要将这些联系起来了:

首先jmm是一组规范,是java提出来的规范,那么java在设计的时候,肯定也符合这组规范。我认为java的内存区域就是根据jmm规范设计的,所以上面说了,他们不属于一个层次,无法类比,只能说是java内存区域,符合jmm的规范。

然后我们也知道,线程是cpu调度的最小单元,也就是说cpu的内核执行线程,上面也说了,执行方法,也就是一个栈帧入栈出栈的过程,那么cpu内核执行线程,就是操作的栈中的数据,那么就是cpu内核把栈中的数据拷贝到它的缓存中去运行。可能有点模糊,我认为你就记住,cpu执行时的数据就是栈中的数据。

你可能在想,那堆中的数据时怎么操作的?我认为是这样:看到这,我也认为电脑面前的你知道了堆在栈中存的是一个地址,那么cpu内核在运行时,不也是根据这个地址找到那个数据了嘛,对cpu来讲,你就是一份数据,在它面前你跟栈中的数据都是一样的,都是数据,然后加到它的缓存中。

如果你读的有点蒙,那就请再读几遍,书读百遍,其义自见:也就是说你在读第一遍的时候,你根本就没理解,你的脑子里只有读过的字,并没有理解到写的含义,打个比方说:我说筷子,你脑海的潜意识中是筷子两根棍的样子,而不是“筷子”这两个字。

然后,请回顾上面提到的两个问题,我也在这贴出来一段代码,以此来表示多核多线程产生的问题:

public class VolatileFaceThread{
    boolean isRunning = true;
    void m() {
        System.out.println("isRunning start");
        while(isRunning) {
        }
        System.out.println("isRunning end");
    }
    public static void main(String\[\] args) {
        VolatileFaceThread vft = new VolatileFaceThread();
        new Thread(vft :: m).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        vft.isRunning = false;
        System.out.println("update isRunning...");
    }
}

预期效果:新启线程会一直循环下去

这段代码,新启的线程将会一直循环下去,不会被停止。试验此段代码时,如果有的实际效果是在主线程修改后,新启的线程也跟着停止了,那么你的电脑可能1核在运行。(当初我在这被卡了好几天,让同事运行时,它运行的实际效果就是预期效果)。

这就是因为,两个线程被两个内核运行,他们把值读取到自己的缓存中运行。而缓存是每个内核私有的,主线程修改了值,对新启线程来说是不可见的,故新启线程会一直循环。

那怎么解决呢,就是让线程可见呗,java中有这一个关键字:volatile-----内存可见性、禁止指令重排序。

被volatile关键字修饰的变量对所有线程总是可见的,也就是在一个线程修改了一个被volatile关键字修饰变量的值,新值总是可以被其他线程立即得知。 

上面说了那么多,就是为了这个结论做铺垫,如果你不明白计算机的内存模型、jmm、java的内存区域,就算说了这个结论,我估计你也应该是一个糊里糊涂的脸,所以,请认真对待自己的疑问。它可以让你坎坷成长,也可以让你退缩安逸。

下次有时间再写一下volatile禁止重排序的功能。

参考博客:https://www.jianshu.com/p/8420ade6ff76

https://blog.csdn.net/javazejian/article/details/72772461

© 著作权归作者所有

粉丝 0
博文 13
码字总数 16181
作品 0
通州
私信 提问
加载中

评论(1)

知其然-更要知其所以然 博主
如有错误请您指正。我会立刻纠正
java并发编程系列-volatile内存实现和原理

前面的博文说了java的内存模型,介绍了java内存模型的基础,此篇文章来说一下volatile关键字,这个在并发编程占有举足轻重地位的关键字。在java5.0 之前它是一个备受争议的关键字,5之后它重获新...

起个名忒难
2017/11/19
0
0
顶级实用干货——谈谈Java中的volatile

内存可见性   volatile是Java提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色。同synchronized相比(synchronized通常称为重量级锁),volatile更轻量级,相比使用s...

java知识分子
2018/10/24
16
0
面试官最爱的volatile关键字

在Java相关的岗位面试中,很多面试官都喜欢考察面试者对Java并发的了解程度,而以volatile关键字作为一个小的切入点,往往可以一问到底,把Java内存模型(JMM),Java并发编程的一些特性都牵...

卡巴拉的树
2017/12/11
0
0
轻量级的同步机制——volatile语义详解(可见性保证+禁止指令重排)

1.关于volatile volatile是java语言中的关键字,用来修饰会被多线程访问的共享变量,是JVM提供的轻量级的同步机制,相比同步代码块或者重入锁有更好的性能。它主要有两重语义,一是保证多个线程...

takumiCX
2018/07/12
0
0
java并发编程,volatile内存实现和原理

前面的博文说了java的内存模型,介绍了java内存模型的基础,此篇文章来说一下volatile关键字,这个在并发编程中,占有举足轻重地位的关键字。 在java5.0 之前它是一个备受争议的关键字,5之后它重...

郑加威
2018/03/01
963
1

没有更多内容

加载失败,请刷新页面

加载更多

Android: Camera1 open、preview、take picture流程分析

一、Camera 架构 NOTE:这是 Android Camera API 1 ,Camera 的架构与 Android 整体架构是保持一致的:Framework : Camera.javaAndroid Runtime : android_hardware_Camera.cppLibrar...

天王盖地虎626
27分钟前
6
0
Spring Boot Actuator监控使用详解

在企业级应用中,学习了如何进行SpringBoot应用的功能开发,以及如何写单元测试、集成测试等还是不够的。在实际的软件开发中还需要:应用程序的监控和管理。SpringBoot的Actuator模块实现了应...

程序新视界
54分钟前
6
0
JDBC+C3P0+DBCP 基本使用

1.概述 这篇文章主要说了JDBC的基本使用,包括Statement,PreparedStatement,JDBC的连接,Mysql创建用户创建数据表,C3P0的连接与配置,DBCP的连接与配置. 2.mysql的处理 这里的JDBC使用Mysql作为...

Blueeeeeee
今天
8
0
MVC Linux下开发及部署

linux使用的是 Ubuntu 64 位 18.04.2 LTS 首先复制C:\Program Files (x86)\Embarcadero\Studio\20.0\PAServer 下 LinuxPAServer20.0.tar.gz 到 linux 目录下 运行链接编译程序 delphi环境配置......

苏兴迎
今天
11
0
3.控件及其属性

1.文本 2.按钮

横着走的螃蟹
今天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部