`
qindongliang1922
  • 浏览: 2152203 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
7265517b-f87e-3137-b62c-5c6e30e26109
证道Lucene4
浏览量:116475
097be4a0-491e-39c0-89ff-3456fadf8262
证道Hadoop
浏览量:124711
41c37529-f6d8-32e4-8563-3b42b2712a50
证道shell编程
浏览量:58715
43832365-bc15-3f5d-b3cd-c9161722a70c
ELK修真
浏览量:70504
社区版块
存档分类
最新评论

Java进阶之内存模型介绍

    博客分类:
  • JAVA
阅读更多
# Java进阶之内存模型介绍

### 前言

不管在什么编程语言里面,读取和写入都是我们程序最普遍的操作,在单线程的程序里面我们可能不关注线程的读写问题,但是一旦到多线程的环境下,读和写就会变得非常敏感。Java内存模型实际上是定义了在多线程环境下使用读和写操作结果一致性的问题。这个模型在JDK5中通过JSR-133议案进行了修订。


### 为什么需要Java内存模型
主要的原因还是在于方便程序员更加关注业务本身还不是底层细节,对程序员来说理解操作系统的内存架构,CPU指令优化,JIT编译器优化是比较困难的一件事。

### 变量的可见性问题

在多核的服务器时代CPU一般都会拥有多级cache,为了提升其处理性能,比如在上篇文章提到过的L1,L2,L3级cache。这种服务器架构的问题主要在于程序里面的共享变量在横跨多个线程时候的可见性问题。对应到我们写的程序里面就是一个线程写完的变量数据,对于其他线程是否可见。在上面文章中提到过每个线程共享进程的主内存,,同时拥有自己的线程local cache,涉及到变量的读写和可见性问题,其实就是线程的local cache与主内存的数据是否一致的问题。在一个多线程累加同一个变量的程序里面,如果一个线程更新了自己local cache的数据,那么必须在更新完把local cache的数据flush到主内存,否则其他线程读取到的数据就有可能是错误的,另一方面其他线程知道主内存的数据可能会更新,那么就必须放弃自己local cache的数据,直接从主内存加载最新版本的数据用来累加,否则就会出现更新结果不正确的情况。(这部分知识现在理解不了,没关系,后面的文章会慢慢梳理。)

### 关于代码指令的重排序问题

为了方便大家理解重排序的概念,我先举个简单的例子:

```java
public static void main(String []args){

int a=3;
int c=4;

int d=a+c;

System.out.printLn(d);

}
```
上面的代码我们看到a变量是先声明的,c变量是后声明的,但在底层编译,或者JIT优化执行的时候,有可能c变量先被解析,然后才是a变量,这就叫指令重排序,目的是为了提高执行效率,当然指令重排序是有约定的,不管执行顺序如何变动(底层优化导致),在单线程中,它的最终结果必须是和代码顺序执行的结果是一致的。如上面的程序,a和c的位置可以互换,但是和d的顺序是不能变的,这就是它的约定,这个在后面的文章会解释。

那么什么是指令重排序,通俗点讲就是:

_*你看到的代码顺序,不一定是它的执行顺序。*_

上面说了,重排序只保证在单线程程序中,不影响最终结果的前提下允许JIT或者硬件指令做一些优化,但是在多线程程序中重排序是可能会导致一些问题的。

### Java内存模型

重排序和变量可见性问题是多线程编程里面的主要问题,Java内存模型主要描述了下面两种情况的的处理:

(1)重排序是底层编译器优化的结果,所以在Java内存模型里面有一些 happens-before 规则来约束重排序,比如说如果前后两个变量有依赖关系如上面例子中的a和d那么它是不能被重排序的,否则一旦重排序,是会导致程序逻辑错误。

(2)对于共享数据的写操作,是没法通过happens-before关系来约束的,如上面说到的累加的例子,此时需要通过Java里面锁的机制来避免。

如下图:



### 关于同步代码块

同步代码块主要完成了两件事情:

(1)对于共享代码在任何时候只保证有一个线程可以操作,这保证了原子性。

(2)lock和unlock操作会触发当前线程flush自己的cache的数据到主内存中,这保证了可见性的问题。


### 关于volatile关键字

在Java里面用volatie关键字修饰共享变量仅仅只保证可见性,仅仅适用于任何时候只有一个线程更新,多个线程读取的业务。所以如果有超过一个线程以上对变量进行修改,那么必须使用锁机制来处理。

所以请大家记住volatile只保证了可见性,不保证原子性。





### 关于final关键字

在Java里面final关键字修饰的变量,仅仅会被初始化一次,后面是不能修改的。

JIT编译器对final的变量会进行优化,如基本类型String,Int,因为这里不存在修改的问题,那也就没有可见性的问题,所以final修饰基本类型变量在多线程的cache里面的是安全的,不需要和主内存有关联,也就不会有flush或者invalidate的情况。

这也是我们经常说Java里面的String为什么是安全的原因,注意使用final修饰的集合框架如List,Set,Map,虽然内存地址不能变,但是里面的内容是可以变的,这里也是不安全的,这一点需要注意。

这也是为什么有一些函数类型的编程语言如Scala里面严格的提供了不可变的集合框架和可变的集合框架,其目的就是为了更加有利于多线程编程。

最后记住final关键字和volatile关键字是不能修饰同一变量的,在IDE的编译器里面是直接会报错的。



### 总结

如果在阅读之前不了解进程和线程在操作系统里面的关系与特点,我建议你先看看前面的两篇文章再阅读本文。本篇文章主要介绍了Java的内存模型相关内容,如果掌握和熟悉了这些知识,那么对于理解和开发并发编程将是非常有帮助的。



有什么问题可以扫码关注微信公众号:我是攻城师(woshigcs) 路漫漫其修远兮,吾将上下而求索










0
0
分享到:
评论

相关推荐

    Java进阶教程,面试大全

    Java进阶教程,面试大全1,可参考以下问题: Semaphore-信号灯机制。 synchronized在静态方法和普通方法的区别。 怎么实现所有线程在等待某个事件的发生才会去执行。 CAS。 Hashtable是怎么加锁的。 HashMap的并发...

    Java进阶教程,面试大全,包罗万象

    Java进阶教程,面试大全1,可参考以下问题: Semaphore-信号灯机制。 synchronized在静态方法和普通方法的区别。 怎么实现所有线程在等待某个事件的发生才会去执行。 CAS。 Hashtable是怎么加锁的。 HashMap的并发...

    Java进阶教程解密JVM视频教程

    4. 了解 Java 内存模型相关知识,见识多线程并发读写共享数据时的问题和 Java 的解决方案。 适应人群 有一定的Java基础,希望提升 Java 内功的人群。 课程亮点 * 系统地学习 JVM 内存结构,垃圾回收、字节码与类...

    java 内存模型

    关于Java内存区的结果,是学习Java进阶不可不知的知识点

    Java全能学习面试手册——Java架构师进阶资料面试资料.zip

    16 27道顶尖的Java多线程、锁、内存模型面试题!.pdf 17 29道常见的Spring面试题!.pdf 18 30个Java经典的集合面试题!.pdf 19 36道面试常问的MyBatis面试题!.pdf 20 40道常问的Java多线程面试题!.pdf 21 55道BAT...

    图解java多线程设计模式

    java.util.concurrent包、synchronized关键字、Swing框架、Java内存模型等内容也均有涉及,不仅能够了解Java多线程的相关知识,还可加深对Java语言的理解。 本书适合以下读者阅读 a.对多线程感兴趣的人 b.对Java...

    使用Java实现一个基于内存的英文全文检索搜索引擎【100012394】

    实现一个基于内存的英文全文检索搜索引擎,需要完成以下功能: 功能 1:将指定目录下的一批.txt 格式的文本文件扫描并在内存里建立倒排索引,...同时也为学生提供了预定义抽象类和接口的 Java API 文档和 UML 模型图。

    java7hashmap源码-AndroidOffer:只为帮助您获得更好的报价

    内存模型 & 内存回收 & 状态机 JVM 类加载机制 垃圾回收算法对比 JVM 内存区域,开线程影响哪块区域内存? [对 Dalvik、ART 虚拟机有什么了解?对比?]( 虚拟机对比.md) 垃圾回收机制和调用 System.gc()的区别? ...

    java8集合源码分析-geektime-java-week-training-camp:极客时间-Java每周训练营

    内存模型、JVM 启动参数详解; JDK 内置命令行工具、JDK 内置图形界面工具、JDWP 简介、JMX 与相关工具; 常见的 JVM GC 算法(Parallel GC/CMS GC/G1 GC)基本原理和特点; 新一代 GC 算法(Java11 ZGC/Java12 ...

    高级java笔试题-AndroidKnowledgeSystem:Android所有的知识体系,面试&进阶&前沿,不断更新

    Android知识体系总结(全方面覆盖Android知识结构,面试&进阶 Version-1.0.1 时间:2018.09) 基本内容 : Android基础知识:基本涵盖Android所有知识体系,四大组件,Fragment,WebView,事件分发,View绘制... Java基础...

    java内核源码-JavaCompass:「Java指南针」为你学习Java指明方向。内容涵盖互联网Java工程师所需要掌握的核心知识,涉及J

    JVM内存模型 类字节码文件深度剖析 垃圾收集机制详解 十种垃圾收集器详解 JVM调优工具详解 GC日志详细分析 JVM调优实战 Mysql性能调优 SQL执行原理详解 索引底层剖析 执行计划与SQL优化 Mysql锁机制与事务隔离级别...

    jcp:Java 并发实践

    Java 并发实践提炼 该存储库旨在存储组织在一本流行书籍讨论的想法、概念和问题的正在进行的工作的结果。 基本面 构建并发应用程序 活性、性能和测试 ...【Java内存模型】(the-java-memory-model.textile)

    基于Springboot + Vue 开发的前后端分离博客(PC端自适应+移动端微信小程序+移动端App)+源代码+文档说明

    2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。...

    代码之美(中文完整版).pdf

    19.2 N维数组的内存模型 19.3NumPy迭代器的起源 19.4 迭代器的设计 19.5 迭代器的接口 19.6 迭代器的使用 19.7 结束语 第20章 NASA火星漫步者任务中的高可靠企业系统 20.1 任务与CIP 20.2 任务需求 20.3 系统架构 ...

    性能测试从零开始:LoadRunner入门与提升

    第10章 循序渐进--进阶LoadRunner高手 282 10.1 性能测试用例的设计策略 282 10.1.1 "普遍撒网,重点查看"的原则 282 10.1.2 保证数据的有效性 284 10.2 LoadRunner高级功能的使用--Web Click Vuser 286 10.2.1 Web ...

    编程新手真言......

    Scheme 程序语言介绍之一 248 与软工有关的哲学 辩证 251 富网页技术 251 形式维度 252 开源与开放 253 Core learning and min learing编程 253 与软工有关的哲学 联系 254 本地化和语言编码 254 学习与维度 255 跟...

    asp.net知识库

    ASP.Net应用程序的多进程模型 NET委托:一个C#睡前故事 [推荐] - [原创] Microsoft .NET策略及框架概述 卸载Class? Web Form 窗体 如何实现web页面的提示保存功能 在ASP.Net中两种利用CSS实现多界面的方法 如何在...

    大数据学习计划.pdf

    2、通过对 Linux ⽂件系统、(⼤数据学习群142974151】内核参数、内存结构、以及 Java 虚 拟机等相关知识的学习,为后续学习分布式 ⽂件系统, Hadoop 集群优化扫清操作系统层 ⾯知识的障碍 2 Hadoop 由许多元素...

    ProgrammersLevelUp:用20年时间跟着皓叔刷“程序员练级攻略”

    异步 I/O 模型和 Lock-Free 编程(系统底层知识) Java 底层知识 数据库 分布式架构入门(分布式架构) 分布式架构经典图书和论文(分布式架构) 分布式架构工程设计(分布式架构) 微服务 容器化和自动化运维 机器...

    一个基于SpringBoot的微服务脚手架+源代码+文档说明

    2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。...

Global site tag (gtag.js) - Google Analytics