转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6536131.html 

一:现代计算机的高速缓存

在计算机组成原理中讲到,现代计算机为了匹配 计算机存储设备的读写速度 与  处理器运算速度,在CPU和内存设备之间加入了一个名为Cache的高速缓存设备来作为缓冲:将运算需要用到的数据从内存复制到cache中,CPU可以在运算期间对cache进行高速的读写操作,运算结束后在从cache把数据同步回内存。

Cache引出了一个新问题:缓存一致性。每个处理器有自己的cache,而他们又共享一个主内存。当多个处理器的运算任务都设计同一内存区域时,将会导致各自的缓存数据不一致。那么,在各自运算结束后,以谁的缓存数据为准同步回主存呢?此时,就需要 缓存一致性协议 来制定多个处理器对统一内存区域的读写操作了。

除了Cache,为了更好地利用处理器资源,处理器还会对代码进行 乱序执行 ,在最后才把结果重组使得结果与顺序执行一致。

二:Java内存模型的类比

类比与现代计算机的主存与cache,JVM中规定了 所有变量都存储在主内存中(类比计算机的主存),然后每条线程有自己的工作内存(类比每个处理器的cache)。线程的工作内存中保存了该线程需要用到的变量的拷贝值,线程在CPU上运行时都是对自己工作线程中的数据进行读写操作,运行结束之后才把数据同步化主内存中。那么类比于计算机使用 缓存一致性协议 解决缓存一致性问题,JVM中就需要线程同步机制来达到多线程对同一内存区域的读写控制了。

此外,Java编译器为了提高性能,采取了 指令重排序(类比计算机的 乱序执行),若多个线程都有语句对同一内存区域进行操作的话,有可能因为指令重排序而导致结果不符预料。因此,也需要线程同步机制来达到多线程对同一内存区域的读写控制

三:主内存与工作内存的数据交互

JVM定义了8种操作来完成主内存与线程工作内存的数据交互:

1:lock:把主内存变量标识为一条线程独占,此时不允许其他线程对此变量进行读写。

2:unlock:解锁一个主内存变量。

3:read:把一个主内存变量值读入到线程的工作内存,强调的是读入这个过程。

4:load:把read到变量值保存到线程工作内存中作为变量副本,强调的是读入的值的保存过程。

5:use:线程执行期间,把工作内存中的变量值传给字节码执行引擎。

6:assign(赋值):字节码执行引擎把运算结果传回工作内存,赋值给工作内存中的结果变量。

7:store:把工作内存中的变量值传送到主内存,强调传送的过程。

8:write:把store传送进来的变量值写入主内存的变量中,强调保存的过程。

JVM要求以上8个操作都具有原子性,即对数据的读写操作具有原子性。但也有例外,即:long、double的非原子性协定:这两个64位类型的数据的读、写操作各需两次进行,一次读/写 32 位,这两次读/两次写 是不保证原子性的。

四:原子性、可见性、有序性

原子性:基本数据类型的读写操作是原子性的;更大范围的(代码块)的原子性可以用lock、unlock操作来实现(上锁后就只有一个线程来执行了,所以不会被其他线程打断原子操作),表现到代码层面就是使用syncrhoized同步块。

可见性:当一个线程修改了被多线程共享的一个主内存变量值时,其他线程能立刻知道这个修改。

我们在上面可以知道,JVM是通过工作内存中的变量值变化后,把新值同步会主内存,然后其他线程从主内存读取这个新值来实现可见性的。这里有个区别:普通变量的值变化后,不一定会立刻同步会主内存,而是会等线程执行完或者一段时间后才同步会,而且同步回主内存后,其他线程的工作内存也不一定会立刻读取新值。而被volatile关键字修饰的变量,一旦在工作内存中被修改,则立刻同步回主内存,并且其他使用了这个变量的线程的工作内存会立刻从主内存读取新值。而syncrhoized关键字修饰的变量由于一次只能有一个线程能使用,故一次也只能有一个工作线程读写它,所以也能“纵向”地实现可见性。

有序性:多线程之间对共享数据的操作的有序性,可以通过volatile和syncrhoized关键字来保证。volatile关键字禁止了指令重排序,而syncrhoized关键字规定了多个线程每次只能有一个线程对共享数据进行操作。

五:volatile关键字

一个volatile变量具有两种特性:可见性、禁止指令重排序。但是,volatile不具备原子性!原因是volatile变量的值可以被多线程交替修改,而修改包括了read、load、use、store、write等过程,这些过程不能保证是原子执行的。

可见性:被volatile关键字修饰的变量,一旦在工作内存中被修改,则立刻同步回主内存,并且其他使用了这个变量的线程的工作内存会立刻从主内存读取新值。

禁止指令重排序:volatile变量在赋值后会创建一个内存屏障:指令重排序时,位于后面的指令不能排到内存屏障之前。

Java多线程中的内存模型的更多相关文章

  1. [心得笔记]Java多线程中的内存模型

    一:现代计算机的高速缓存 在计算机组成原理中讲到,现代计算机为了匹配 计算机存储设备的读写速度 与  处理器运算速度,在CPU和内存设备之间加入了一个名为Cache的高速缓存设备来作为缓冲:将运算需要 ...

  2. java多线程中的三种特性

    java多线程中的三种特性 原子性(Atomicity) 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行. 如果一个操作时原子性的,那么多线程并 ...

  3. (转载)JVM中的内存模型与垃圾回收

    转载自微信公众号:Java高级架构(Java-jiagou)-----看完这篇文章,我奶奶都知道JVM中的内存模型与垃圾回收了! 六.内存模型 6.1  内存模型与运行时数据区 Java虚拟机在执行J ...

  4. 【java虚拟机】jvm内存模型

    作者:pengjunlee原文链接:https://blog.csdn.net/pengjunlee/article/details/71909239 目录 一.运行时数据区域 1.程序计数器 2.J ...

  5. Java虚拟机学习 - 体系结构 内存模型

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代” .“非堆”, 它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内存区域.默认最小值为16MB,最 ...

  6. Java虚拟机中的内存分配

    java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途以及创建和销毁的时间. 栈:存放的是局部变量,包括:1.用来保存基本数据类型的值:2.保存类 ...

  7. Java虚拟机学习 - 体系结构 内存模型(1)

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代" ."非堆",  它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内 ...

  8. Java虚拟机学习 - 体系结构 内存模型(转载)

    一:Java技术体系模块图 二:JVM内存区域模型 1.方法区 也称"永久代” .“非堆”,  它用于存储虚拟机加载的类信息.常量.静态变量.是各个线程共享的内存区域.默认最小值为16MB, ...

  9. Java:JVM的内存模型

    JVM内存模型 JVM内存模型可以分为两个部分,如下图所示,堆和方法区是所有线程共有的,而虚拟机栈,本地方法栈和程序计数器则是线程私有的.   1. 堆(Heap) 堆内存是所有线程共有的,可以分为两 ...

随机推荐

  1. 蓝精灵:寻找神秘村Smurfs: The Lost Village迅雷下载

    蓝妹妹(黛米·洛瓦托 Demi Lovato 配音)发现了一张遗落的地图,由此引发精灵们对于神秘村庄真实性的猜想.于是,满怀好奇心的蓝妹妹与聪聪(丹尼·朴迪 Danny Pudi 配音).笨笨(杰克· ...

  2. Button 在布局文件中定义监听器,文字阴影,自定义图片,代码绘制样式,添加音效的方法

    1.Button自己在xml文件中绑定监听器 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/andro ...

  3. 轻松看懂Java字节码

    java字节码 计算机只认识0和1.这意味着任何语言编写的程序最终都需要经过编译器编译成机器码才能被计算机执行.所以,我们所编写的程序在不同的平台上运行前都要经过重新编译才能被执行. 而Java刚诞生 ...

  4. SIFT(Scale-invariant feature transform) & HOG(histogram of oriented gradients)

    SIFT :scale invariant feature transform HOG:histogram of oriented gradients 这两种方法都是基于图像中梯度的方向直方图的特征提 ...

  5. golang的配置文件操作:viper

    参考: 1.http://blog.51cto.com/13599072/2072753 2.https://studygolang.com/articles/14453 3.https://www. ...

  6. postgresql数据库用户名密码验证失败

    问题分析 (1)检查环境变量中是否指定了相关参数,比如: #export PGUSER=highgo#export PGDATABASE=highgo#export PGHOST=localhost# ...

  7. Asp.Net验证控件浅析

    ASP.NET公有六种验证控件,分别如下: 控件名           功能描叙  RequiredFieldValidator(必须字段验证) 用于检查是否有输入值  CompareValidato ...

  8. Asp.net WebApi版本控制

    有关web api的版本控制网上有很多,如Web API 版本控制的几种方式 Web API 版本化的介绍 但是具体的code并不多,或者说可以run的demo 不多. 版本控制如果项目一开始还好做关 ...

  9. ajax file upload 修改

    先前 写过 JS 判断上传 文件 大小 后来发现一个问题, 就是单页面运行 js 没有问题, 但是基础呢个到项目中 有些时候 obj_img.dynsrc = file.value; 报错说没有 权限 ...

  10. [leetcode]Remove Duplicates from Sorted List @ Python

    原题地址:https://oj.leetcode.com/problems/remove-duplicates-from-sorted-list/ 题意: Given a sorted linked ...