Java并发以内存模型

Java是一门支持多线程执行的语言,要编写正确的并发程序,了解Java内存模型是重要前提。而了解硬件内存模型有助于理解程序的执行。html

本文主要整理如下内容java

  • Java内存模型
  • 硬件内存架构
  • 共享对象可见性
  • 竞争条件

Java内存模型

Java内存模型最新修订是在Java5。 JSR-176 罗列了 J2SE5.0 相关发布特性,包含其中的 JSR-133(JavaTM内存模型与线程规范),java虚拟机遵循此规范。延续至今该内存模型在Java8中依然奏效。缓存

JSR 全称 Java Specification Requests,意为Java标准化技术规范的正式请求。

Java程序运行在虚拟机上(Jvm)。从逻辑角度看,Jvm内存被划分为线程堆栈。每一个线程都拥有本身的堆栈,该线程堆栈存储的数据不对其它线程可见。堆内存用于存储共享数据。多线程

堆栈模型

线程堆栈存储方法中全部局部变量,包含原始类型(boolean,byte,short,char,int,long, float,double)和对象引用。架构

堆存储须要共享对象和静态变量。并发

注意 对象不必定都会存储到堆内存。看下面例子,假若是Object对象不须要被其它线程共享,编译器会执行堆分配转化为栈分配性能

解释一下,编译器会根据 对象是否逃逸作出优化。优化的其中一项就是 堆分配转化为栈分配,目的在于减轻GC压力,提高性能。此优化动做由Jvm参数-XX:+DoEscapeAnalysi 进行控制。Java8 默认开启。

测试:经过开启或关闭 -XX:+PrintGC -XX:-DoEscapeAnalysis 观察是否执行GC来判断对象存储位置。学习

public static void main(String[] args){
        for(int i = 0; i < 10000000; i++){
            createObj();
        }
    }
    public static void createObj(){
        new Object();
    }

堆栈模型

硬件内存架构

以下图,现代计算机一般都装有2个或者更多的CPU,CPU又能够是多核。一个CPU包含一组寄存器,每一个CPU具备一个高速缓存,而高速缓存又分为L1,L2,L3,L4 不一样层级缓存。测试

RAM为主存储也就是咱们说的计算机内存,全部CPU均可以读取主存储。优化

当CPU读取主存储数据时,它会将部分主存储数据读入CPU高速缓存中,又将缓存的中一部分读入寄存器执行,操做结束后,将值从寄存器刷新到高速缓存中,高速缓存在特定的时刻将数据统一刷新到内存中。

硬件内存架构

实际执行

事实上,上面阐述的Java堆栈内存模型是为了理解抽象出来的。实际执行就像下图同样,线程栈和堆的数据可能分散到硬件不一样的存储区域。数据分散在不一样区域会带来如下两个主要问题。

堆栈模型与硬件内存架构关联

共享对象可见性

下面场景两个线程同时操做对象obj.count,其中一个线程对obj.count进行更新,可是对其它线程不可见。

线程A操做obj时,先从主存里拷贝一个数据副本到CPU高速缓存,又到寄存器,而后修改obj.count=2后刷新到CPU高速缓存,可是数据暂未同步到主存。以此同时线程B也操做obj,拷贝的数据副本仍然为obj.count=1,这会致使程序结果错误。

解决此问题,可使用Java volatile关键字。volatile可简单理解为跳过CPU高速缓存,让修改结果及时同步到主存,从而保证了其它线程读到最新值。volatile 后期专门介绍。

共享对象可见性

竞争条件

另一种状况假若是多个线程同时更行obj.count,这时会发生竞争条件

解决方法,使用Java synchronized 保证线程执行顺序,另外synchronized包裹中的全部变量都直接从主存读取(跳过CPU高速缓存),而且当线程退出synchronized后,全部更新的变量将同步到主存。

竞争条件

总结

本文记录Java内存模型,其中主要内容来源于 Jakob Jenkov 大神博客。

欢迎你们留言交流,一块儿学习分享!!!

http://tutorials.jenkov.com/j...
相关文章
相关标签/搜索