Java多线程编程笔记4:Java内存模型
Java内存模型
Java内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的内存访问效果。
处理器上的寄存器读写速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。同时产生了缓存一致性问题,如果多个缓存共享同一块主内存区域,多个缓存的数据可能不一致,需要协议来解决这个问题。
所有的变量存储在主内存中,每个线程有自己的工作内存,工作内存存储在高速缓存或寄存器中,保存了该线程使用的变量的主内存副本拷贝。线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成。
内存间交互
Java内存模型定义了8个操作来完成主内存和工作内存的交互操作。
- read:把一个变量的值从主内存传输到工作内存中
- load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
- use:把工作内存中一个变量的值传递给执行引擎
- assign:把一个从执行引擎接收到的值赋给工作内存的变量
- store:把工作内存的一个变量的值传送到主内存中
- write:在 store 之后执行,把 store 得到的值放入主内存的变量中
- lock:作用于主内存的变量
- unlock
内存模型的三大特性
1. 原子性
原子性就是指一个操作中要么全部执行成功,否则失败。Java内存模型保证了上述的八个操作具有原子性。但是Java内存模型允许虚拟机将没有被volatile修饰的64位数据(long,double)的读写操作划分为两次32位操作进行,因此load,store,read和write操作不具备原子性。但是JMM只保证了上述操作的原子性,像是i++这样的操作,其实是分为获取i,i自增以及赋值给i三步的,如果要实现这样的原子操作就需要使用原子类实现,或者也可以使用它synchronized互斥锁来保证操作的原子性。
2. 可见性
可见性指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值来实现可见性的。
可见性的三种是实现方式:
- volatile
- synchronized,对一个变量执行unlock操作之前,必须把变量值同步回主内存。
- final,被 final关键字修饰的字段在构造器中一旦初始化完成,并且没有发生 this 逃逸(其它线程通过 this 引用访问到初始化了一半的对象),那么其它线程就能看见 final 字段的值。
使用 volatile 关键词修饰的变量每次读取都会得到最新的数据,不管哪个线程对这个变量的修改都会立即刷新到主内存。但是 volatile 关键字不能保证操作的原子性。
synchronized和加锁也能能保证可见性,实现原理就是在释放锁之前其余线程是访问不到这个共享变量的。但是和 volatile 相比开销较大。
3. 顺序性
假设有三个语句a,b,c。在同一个线程内,操作是有序的,以a->b->c的顺序执行。但是,JMM在保证最终结果和代码顺序执行结果一致的情况下,为了提高整体效率会进行指令重排。在单线程中重排不会问题,在多小县城中,可能会有数据不一致的问题。
Java中可以使用volatile关键字来保证顺序性,还可以用synchronized和lock来保证。
volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。
通过 synchronized 和 lock 来保证有序性,它保证每个时刻只有一个线程执行同步代码,相当于是让线程顺序执行同步代码。
JVM通过happen-before来保证顺序性
除了使用volatile和synchronized保证顺序性,JVM还规定了先行发生原则,让一个操作无需控制就能先于另一个操作完成。
- 单一线程原则:在一个线程内,程序前面的操作先于后面的操作。
- 管程锁定规则:一个unlock操作先于后面对同一个锁的lock操作发生。
- volatile变量规则:对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作,也就是说读取的值肯定是最新的。
- 线程启动规则:Thread对象的start()方法调用先行发生于此线程的每一个动作。
- 线程加入规则:Thread 对象的结束先行发生于 join() 方法返回。
- 线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。
- 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法的开始。
- 传递性:如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。
参考资料
- github.com/CyC2018/CS-…
- crossoverjie.top/JCSprout/#/…
Java多线程编程笔记4:Java内存模型相关推荐
- java多线程编程_《java多线程编程实战指南》读书笔记 -- 基本概念
展开 并发:多个线程操作相同资源,保证线程安全,合理使用资源 高并发:服务能同时处理多个请求,提高程序性能 测试上下文切换工具 Lmbench3 测量上下文切换时长 vmstat 测量上下文切换次数 ...
- 深入理解Java虚拟机学习笔记-1.JVM内存模型
JVM内存模型 1.内存模型结构图 名称 特征 作用 配置参数 异常 程序计数器 占用内存小,线程私有, 生命周期与线程相同 大致为字节码行号指示器 无 无 虚拟机栈 线程私有,生命周期与线程相同,使 ...
- java 静态代码块 多线程,Java多线程编程笔记10:单例模式
立即加载:"饿汉模式" 立即加载就是指使用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化.也就是在调用方法前,实例就被创建了.示例代码如下所示: class MyO ...
- Java多线程编程笔记之Condition
本篇内容基于JDK7,涉及Condition常用方法. 1.概述 Condition接口位于java.util.concurrent.locks包下,实现类有 AbstractQueuedLongSy ...
- Java并发编程笔记之ThreadLocal内存泄漏探究
使用 ThreadLocal 不当可能会导致内存泄露,是什么原因导致的内存泄漏呢? 我们首先看一个例子,代码如下: /*** Created by cong on 2018/7/14.*/ publi ...
- 《Java多线程编程核心技术》读书笔记
为什么80%的码农都做不了架构师?>>> <Java多线程编程核心技术>读书笔记. ###第一章 Java多线程技能 使用Java多线程两种方式. 继承Thread ...
- java多线程编程01---------基本概念
一. java多线程编程基本概念--------基本概念 java多线程可以说是java基础中相对较难的部分,尤其是对于小白,次一系列文章的将会对多线程编程及其原理进行介绍,希望对正在多线程中碰壁的小 ...
- Java多线程编程那些事:volatile解惑--转
http://www.infoq.com/cn/articles/java-multi-thread-volatile/ 1. 前言 volatile关键字可能是Java开发人员"熟悉而又陌 ...
- Java多线程编程模式实战指南(二):Immutable Object模式--转载
本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-immutable-o ...
最新文章
- Windows下开源缺陷跟踪系统mantis安装指南(续)-
- html弹窗确认取消公告代码,js 弹出确认与取消对话框的四种方法
- 添加用户信息的方法java_添加用户的流程分析
- 飞书上点链接怎么指定跳转浏览器_链接示例表功能还可以这样用??
- 【spring boot】支持webjars
- Redis集群一致性Hash效果的代码演示
- Ubuntu 安装R/Rstudio
- NOIP 2013 day2
- 邮件里直接显示图片_利用邮件合并带图片功能批量制作准考证、工作证、成绩通知单等...
- vue2.x使用jsoneditor编辑器
- 体脂率在线计算机,如何简单测算出自己的体脂率?
- pageSize不生效
- ubuntu--制作图标
- WPF入门教程(一)---基础
- 如何设计好的RESTful API?
- 单元格等于计算机日期,Excel相邻单元格快速填入相同日期的几种方法
- python爬虫,虎牙房间爬取(selenium)
- 次世代游戏建模师一个月的工资有多少?
- 用AI给黑白照片上色,复现记忆中的旧时光
- Android7.1 Kyguard界面灭屏时间分析