精华内容
下载资源
问答
  • 全面理解Java内存模型

    万次阅读 多人点赞 2016-09-21 18:39:21
    Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。如果我们要想深入了解Java并发编程,就要先理解好Java内存...

    Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。

    如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的时候对共享变量进行同步。原始的Java内存模型效率并不是很理想,因此Java1.5版本对其进行了重构,现在的Java8仍沿用了Java1.5的版本。

    关于并发编程

    在并发编程领域,有两个关键问题:线程之间的通信同步

    线程之间的通信

    线程的通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种共享内存消息传递

    共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信。

    消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信,在java中典型的消息传递方式就是wait()notify()

    关于Java线程之间的通信,可以参考线程之间的通信(thread signal)

    线程之间的同步

    同步是指程序用于控制不同线程之间操作发生相对顺序的机制。

    在共享内存并发模型里,同步是显式进行的。程序员必须显式指定某个方法或某段代码需要在线程之间互斥执行。

    在消息传递的并发模型里,由于消息的发送必须在消息的接收之前,因此同步是隐式进行的。

    Java的并发采用的是共享内存模型

    Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。如果编写多线程程序的Java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。

    Java内存模型

    上面讲到了Java线程之间的通信采用的是过共享内存模型,这里提到的共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

    这里写图片描述

    从上图来看,线程A与线程B之间如要通信的话,必须要经历下面2个步骤:

    1. 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。
    2. 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。 
    

    下面通过示意图来说明这两个步骤:
    这里写图片描述

    如上图所示,本地内存A和B有主内存中共享变量x的副本。假设初始时,这三个内存中的x值都为0。线程A在执行时,把更新后的x值(假设值为1)临时存放在自己的本地内存A中。当线程A和线程B需要通信时,线程A首先会把自己本地内存中修改后的x值刷新到主内存中,此时主内存中的x值变为了1。随后,线程B到主内存中去读取线程A更新后的x值,此时线程B的本地内存的x值也变为了1。

    从整体来看,这两个步骤实质上是线程A在向线程B发送消息,而且这个通信过程必须要经过主内存。JMM通过控制主内存与每个线程的本地内存之间的交互,来为java程序员提供内存可见性保证。

    上面也说到了,Java内存模型只是一个抽象概念,那么它在Java中具体是怎么工作的呢?为了更好的理解上Java内存模型工作方式,下面就JVM对Java内存模型的实现、硬件内存模型及它们之间的桥接做详细介绍。

    JVM对Java内存模型的实现

    在JVM内部,Java内存模型把内存分成了两部分:线程栈区和堆区,下图展示了Java内存模型在JVM中的逻辑视图:
    这里写图片描述
    JVM中运行的每个线程都拥有自己的线程栈,线程栈包含了当前线程执行的方法调用相关信息,我们也把它称作调用栈。随着代码的不断执行,调用栈会不断变化。

    线程栈还包含了当前方法的所有本地变量信息。一个线程只能读取自己的线程栈,也就是说,线程中的本地变量对其它线程是不可见的。即使两个线程执行的是同一段代码,它们也会各自在自己的线程栈中创建本地变量,因此,每个线程中的本地变量都会有自己的版本。

    所有原始类型(boolean,byte,short,char,int,long,float,double)的本地变量都直接保存在线程栈当中,对于它们的值各个线程之间都是独立的。对于原始类型的本地变量,一个线程可以传递一个副本给另一个线程,当它们之间是无法共享的。

    堆区包含了Java应用创建的所有对象信息,不管对象是哪个线程创建的,其中的对象包括原始类型的封装类(如Byte、Integer、Long等等)。不管对象是属于一个成员变量还是方法中的本地变量,它都会被存储在堆区。

    下图展示了调用栈和本地变量都存储在栈区,对象都存储在堆区:
    这里写图片描述
    一个本地变量如果是原始类型,那么它会被完全存储到栈区。
    一个本地变量也有可能是一个对象的引用,这种情况下,这个本地引用会被存储到栈中,但是对象本身仍然存储在堆区。

    对于一个对象的成员方法,这些方法中包含本地变量,仍需要存储在栈区,即使它们所属的对象在堆区。
    对于一个对象的成员变量,不管它是原始类型还是包装类型,都会被存储到堆区。

    Static类型的变量以及类本身相关信息都会随着类本身存储在堆区。

    堆中的对象可以被多线程共享。如果一个线程获得一个对象的应用,它便可访问这个对象的成员变量。如果两个线程同时调用了同一个对象的同一个方法,那么这两个线程便可同时访问这个对象的成员变量,但是对于本地变量,每个线程都会拷贝一份到自己的线程栈中。

    下图展示了上面描述的过程:
    这里写图片描述

    硬件内存架构

    不管是什么内存模型,最终还是运行在计算机硬件上的,所以我们有必要了解计算机硬件内存架构,下图就简单描述了当代计算机硬件内存架构:
    这里写图片描述

    现代计算机一般都有2个以上CPU,而且每个CPU还有可能包含多个核心。因此,如果我们的应用是多线程的话,这些线程可能会在各个CPU核心中并行运行。

    在CPU内部有一组CPU寄存器,也就是CPU的储存器。CPU操作寄存器的速度要比操作计算机主存快的多。在主存和CPU寄存器之间还存在一个CPU缓存,CPU操作CPU缓存的速度快于主存但慢于CPU寄存器。某些CPU可能有多个缓存层(一级缓存和二级缓存)。计算机的主存也称作RAM,所有的CPU都能够访问主存,而且主存比上面提到的缓存和寄存器大很多。

    当一个CPU需要访问主存时,会先读取一部分主存数据到CPU缓存,进而在读取CPU缓存到寄存器。当CPU需要写数据到主存时,同样会先flush寄存器到CPU缓存,然后再在某些节点把缓存数据flush到主存。

    Java内存模型和硬件架构之间的桥接

    正如上面讲到的,Java内存模型和硬件内存架构并不一致。硬件内存架构中并没有区分栈和堆,从硬件上看,不管是栈还是堆,大部分数据都会存到主存中,当然一部分栈和堆的数据也有可能会存到CPU寄存器中,如下图所示,Java内存模型和计算机硬件内存架构是一个交叉关系:
    这里写图片描述
    当对象和变量存储到计算机的各个内存区域时,必然会面临一些问题,其中最主要的两个问题是:

    1. 共享对象对各个线程的可见性
    2. 共享对象的竞争现象
    

    共享对象的可见性

    当多个线程同时操作同一个共享对象时,如果没有合理的使用volatile和synchronization关键字,一个线程对共享对象的更新有可能导致其它线程不可见。

    想象一下我们的共享对象存储在主存,一个CPU中的线程读取主存数据到CPU缓存,然后对共享对象做了更改,但CPU缓存中的更改后的对象还没有flush到主存,此时线程对共享对象的更改对其它CPU中的线程是不可见的。最终就是每个线程最终都会拷贝共享对象,而且拷贝的对象位于不同的CPU缓存中。

    下图展示了上面描述的过程。左边CPU中运行的线程从主存中拷贝共享对象obj到它的CPU缓存,把对象obj的count变量改为2。但这个变更对运行在右边CPU中的线程不可见,因为这个更改还没有flush到主存中:
    这里写图片描述
    要解决共享对象可见性这个问题,我们可以使用java volatile关键字。 Java’s volatile keyword. volatile 关键字可以保证变量会直接从主存读取,而对变量的更新也会直接写到主存。volatile原理是基于CPU内存屏障指令实现的,后面会讲到。

    竞争现象

    如果多个线程共享一个对象,如果它们同时修改这个共享对象,这就产生了竞争现象。

    如下图所示,线程A和线程B共享一个对象obj。假设线程A从主存读取Obj.count变量到自己的CPU缓存,同时,线程B也读取了Obj.count变量到它的CPU缓存,并且这两个线程都对Obj.count做了加1操作。此时,Obj.count加1操作被执行了两次,不过都在不同的CPU缓存中。

    如果这两个加1操作是串行执行的,那么Obj.count变量便会在原始值上加2,最终主存中的Obj.count的值会是3。然而下图中两个加1操作是并行的,不管是线程A还是线程B先flush计算结果到主存,最终主存中的Obj.count只会增加1次变成2,尽管一共有两次加1操作。
    这里写图片描述

    要解决上面的问题我们可以使用java synchronized代码块。synchronized代码块可以保证同一个时刻只能有一个线程进入代码竞争区,synchronized代码块也能保证代码块中所有变量都将会从主存中读,当线程退出代码块时,对所有变量的更新将会flush到主存,不管这些变量是不是volatile类型的。

    volatile和 synchronized区别

    详细请见 volatile和synchronized的区别

    支撑Java内存模型的基础原理

    指令重排序

    在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。但是,JMM确保在不同的编译器和不同的处理器平台之上,通过插入特定类型的Memory Barrier来禁止特定类型的编译器重排序和处理器重排序,为上层提供一致的内存可见性保证。

    1. 编译器优化重排序:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
    2. 指令级并行的重排序:如果不存l在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
    3. 内存系统的重排序:处理器使用缓存和读写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。

    数据依赖性

    如果两个操作访问同一个变量,其中一个为写操作,此时这两个操作之间存在数据依赖性。
    编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序,即不会重排序。

    as-if-serial

    不管怎么重排序,单线程下的执行结果不能被改变,编译器、runtime和处理器都必须遵守as-if-serial语义。

    内存屏障(Memory Barrier )

    上面讲到了,通过内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:

    1. 保证特定操作的执行顺序。
    2. 影响某些数据(或则是某条指令的执行结果)的内存可见性。

    编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。

    Memory Barrier所做的另外一件事是强制刷出各种CPU cache,如一个Write-Barrier(写入屏障)将刷出所有在Barrier之前写入 cache 的数据,因此,任何CPU上的线程都能读取到这些数据的最新版本。

    这和java有什么关系?上面java内存模型中讲到的volatile是基于Memory Barrier实现的。

    如果一个变量是volatile修饰的,JMM会在写入这个字段之后插进一个Write-Barrier指令,并在读这个字段之前插入一个Read-Barrier指令。这意味着,如果写入一个volatile变量,就可以保证:

    1. 一个线程写入变量a后,任何线程访问该变量都会拿到最新值。
    2. 在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为Memory Barrier会刷出cache中的所有先前的写入。

    happens-before

    从jdk5开始,java使用新的JSR-133内存模型,基于happens-before的概念来阐述操作之间的内存可见性。

    在JMM中,如果一个操作的执行结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,这个的两个操作既可以在同一个线程,也可以在不同的两个线程中。

    与程序员密切相关的happens-before规则如下:

    1. 程序顺序规则:一个线程中的每个操作,happens-before于该线程中任意的后续操作。
    2. 监视器锁规则:对一个锁的解锁操作,happens-before于随后对这个锁的加锁操作。
    3. volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读。
    4. 传递性规则:如果 A happens-before B,且 B happens-before C,那么A happens-before C。

    注意:两个操作之间具有happens-before关系,并不意味前一个操作必须要在后一个操作之前执行!仅仅要求前一个操作的执行结果,对于后一个操作是可见的,且前一个操作按顺序排在后一个操作之前。

    参考文档 :
    1. http://www.infoq.com/cn/articles/java-memory-model-1
    2. http://www.jianshu.com/p/d3fda02d4cae

    展开全文
  • Java进阶(一)Java内存解析

    万次阅读 多人点赞 2015-04-21 20:45:58
    栈、堆、常量池等虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同。本文将深入Java核心,简单讲解Java内存分配方面的知识。 首先我们先来讲解一下内存中的各个区域。 stack(栈):存放基本类型的...

        栈、堆、常量池等虽同属Java内存分配时操作的区域,但其适用范围和功用却大不相同。本文将深入Java核心,简单讲解Java内存分配方面的知识。

        首先我们先来讲解一下内存中的各个区域。

    stack(栈)

        存放基本类型的数据和对象的引用,即存放变量。

        如果存放的是基本类型数据(非静态变量),则直接将变量名和值存入stack中的内存中;

        如果是引用类型,则将变量名存入栈,然后指向它new出的对象(存放在堆中)。

    heap(堆)

       存放new产生的数据。

    data segment

       分为静态域和常量域。

    静态域

       存放在对象中用static定义的静态成员(即静态变量,同理,如果该静态变量是基本类型则将变量名和值存入静态域,如果是引用类型则指向new出的对象)。

    常量池

       常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:类和接口的全限定名;字段的名称和描述符;方法和名称和描述符。

       code segment:存放代码。

       下面我来举几个简单的小例子来让大家认识一下这几个区域具体存放的内容:

       注:例1和例2中大家要注意String类型不是基本数据类型,所以它可以创建对象,并且它的值是放在常量池中的。

    例1. 非静态和静态变量的创建

       String str = "abc";  static Int a = 10;  //创建String类型的变量str;创建Int类型变量a

       内存解析:

       这里的str是非静态变量所以将其放入到栈中,而静态变量a则需要放入静态域中,对于变量值"abc"和10则都应该放入到常量池当中

    例2. 对象的创建

       String str=new String("abc");  //创建String类型的对象str

       内存解析:

       这里的str是String类型的对象,所以放入栈中,而new出来的东西则放入到堆中,对于初始值"abc"则放入到常量池中即可。

     

    例3. 数组的创建

       Int s[] = new Int[3];s[0]=3;s[1]=2;s[2]=3;

       内存解析:

       这里的s[]是一个数组变量,所以放入到栈中,new出来的东西还是放入到堆中,最后的每个元素的值则放入到常量池当中。

     

    展开全文
  • java内存模型JMM理解整理

    万次阅读 2020-01-07 14:44:03
    什么是JMM  JMM即为JAVA 内存模型(java memory model)。...Java内存模型,就是为了屏蔽系统和硬件的差异,让一套代码在不同平台下能到达相同的访问结果。JMM从java 5开始的JSR-133发布后,已经...

     

      什么是JMM

      JMM即为JAVA 内存模型(java memory model)。因为在不同的硬件生产商和不同的操作系统下,内存的访问逻辑有一定的差异,结果就是当你的代码在某个系统环境下运行良好,并且线程安全,但是换了个系统就出现各种问题。Java内存模型,就是为了屏蔽系统和硬件的差异,让一套代码在不同平台下能到达相同的访问结果。JMM从java 5开始的JSR-133发布后,已经成熟和完善起来。

      内存划分

      JMM规定了内存主要划分为主内存和工作内存两种。此处的主内存和工作内存跟JVM内存划分(堆、栈、方法区)是在不同的层次上进行的,如果非要对应起来,主内存对应的是Java堆中的对象实例部分,工作内存对应的是栈中的部分区域,从更底层的来说,主内存对应的是硬件的物理内存,工作内存对应的是寄存器和高速缓存。

      JVM在设计时候考虑到,如果JAVA线程每次读取和写入变量都直接操作主内存,对性能影响比较大,所以每条线程拥有各自的工作内存,工作内存中的变量是主内存中的一份拷贝,线程对变量的读取和写入,直接在工作内存中操作,而不能直接去操作主内存中的变量。但是这样就会出现一个问题,当一个线程修改了自己工作内存中变量,对其他线程是不可见的,会导致线程不安全的问题。因为JMM制定了一套标准来保证开发者在编写多线程程序的时候,能够控制什么时候内存会被同步给其他线程。

      内存交互操作

       内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

      • lock     (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
      • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
      • read    (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
      • load     (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
      • use      (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
      • assign  (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
      • store    (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
      • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

      JMM对这八种指令的使用,制定了如下规则:

      • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
      • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
      • 不允许一个线程将没有assign的数据从工作内存同步回主内存
      • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
      • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
      • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
      • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
      • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

      JMM对这八种操作规则和对volatile的一些特殊规则就能确定哪里操作是线程安全,哪些操作是线程不安全的了。但是这些规则实在复杂,很难在实践中直接分析。所以一般我们也不会通过上述规则进行分析。更多的时候,使用java的happen-before规则来进行分析。

      模型特征

      原子性:例如上面八项操作,在操作系统里面是不可分割的单元。被synchronized关键字或其他锁包裹起来的操作也可以认为是原子的。从一个线程观察另外一个线程的时候,看到的都是一个个原子性的操作。

    1         synchronized (this) {
    2             a=1;
    3             b=2;
    4         }

      例如一个线程观察另外一个线程执行上面的代码,只能看到a、b都被赋值成功结果,或者a、b都尚未被赋值的结果。

      可见性:每个工作线程都有自己的工作内存,所以当某个线程修改完某个变量之后,在其他的线程中,未必能观察到该变量已经被修改。volatile关键字要求被修改之后的变量要求立即更新到主内存,每次使用前从主内存处进行读取。因此volatile可以保证可见性。除了volatile以外,synchronized和final也能实现可见性。synchronized保证unlock之前必须先把变量刷新回主内存。final修饰的字段在构造器中一旦完成初始化,并且构造器没有this逸出,那么其他线程就能看到final字段的值。

      有序性:java的有序性跟线程相关。如果在线程内部观察,会发现当前线程的一切操作都是有序的。如果在线程的外部来观察的话,会发现线程的所有操作都是无序的。因为JMM的工作内存和主内存之间存在延迟,而且java会对一些指令进行重新排序。volatile和synchronized可以保证程序的有序性,很多程序员只理解这两个关键字的执行互斥,而没有很好的理解到volatile和synchronized也能保证指令不进行重排序。

      Volatile内存语义

       volatile的一些特殊规则

      Final域的内存语义

      被final修饰的变量,相比普通变量,内存语义有一些不同。具体如下:

      • JMM禁止把Final域的写重排序到构造器的外部。
      • 在一个线程中,初次读该对象和读该对象下的Final域,JMM禁止处理器重新排序这两个操作。

    复制代码

     1 public class FinalConstructor {
     2 
     3     final int a;
     4 
     5     int b;
     6 
     7     static FinalConstructor finalConstructor;
     8 
     9     public FinalConstructor() {
    10         a = 1;
    11         b = 2;
    12     }
    13 
    14     public static void write() {
    15         finalConstructor = new FinalConstructor();
    16     }
    17 
    18     public static void read() {
    19         FinalConstructor constructor = finalConstructor;
    20         int A = constructor.a;
    21         int B = constructor.b;
    22     }
    23 }

    复制代码

      假设现在有线程A执行FinalConstructor.write()方法,线程B执行FinalConstructor.read()方法。

      对应上述的Final的第一条规则,因为JMM禁止把Final域的写重排序到构造器的外部,而对普通变量没有这种限制,所以变量A=1,而变量B可能会等于2(构造完成),也有可能等于0(第11行代码被重排序到构造器的外部)。

       对应上述的Final的第二条规则,如果constructor的引用不为null,A必然为1,要么constructor为null,抛出空指针异常。保证读final域之前,一定会先读该对象的引用。但是普通对象就没有这种规则。

      (上述的Final规则反复测试,遗憾的是我并没有能模拟出来普通变量不能正常构造的结果)

      Happen-Before(先行发生规则)

      在常规的开发中,如果我们通过上述规则来分析一个并发程序是否安全,估计脑壳会很疼。因为更多时候,我们是分析一个并发程序是否安全,其实都依赖Happen-Before原则进行分析。Happen-Before被翻译成先行发生原则,意思就是当A操作先行发生于B操作,则在发生B操作的时候,操作A产生的影响能被B观察到,“影响”包括修改了内存中的共享变量的值、发送了消息、调用了方法等。

      Happen-Before的规则有以下几条

      • 程序次序规则(Program Order Rule):在一个线程内,程序的执行规则跟程序的书写规则是一致的,从上往下执行。
      • 管程锁定规则(Monitor Lock Rule):一个Unlock的操作肯定先于下一次Lock的操作。这里必须是同一个锁。同理我们可以认为在synchronized同步同一个锁的时候,锁内先行执行的代码,对后续同步该锁的线程来说是完全可见的。
      • volatile变量规则(volatile Variable Rule):对同一个volatile的变量,先行发生的写操作,肯定早于后续发生的读操作
      • 线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的没一个动作
      • 线程中止规则(Thread Termination Rule):Thread对象的中止检测(如:Thread.join(),Thread.isAlive()等)操作,必行晚于线程中所有操作
      • 线程中断规则(Thread Interruption Rule):对线程的interruption()调用,先于被调用的线程检测中断事件(Thread.interrupted())的发生
      • 对象中止规则(Finalizer Rule):一个对象的初始化方法先于一个方法执行Finalizer()方法
      • 传递性(Transitivity):如果操作A先于操作B、操作B先于操作C,则操作A先于操作C

      以上就是Happen-Before中的规则。通过这些条件的判定,仍然很难判断一个线程是否能安全执行,毕竟在我们的时候线程安全多数依赖于工具类的安全性来保证。想提高自己对线程是否安全的判断能力,必然需要理解所使用的框架或者工具的实现,并积累线程安全的经验

    展开全文
  • JAVA内存结构和JAVA内存模型

    千次阅读 2018-01-18 12:01:43
    JAVA内存结构:堆、栈、方法区; 堆:存放所有 new出来的东西(堆空间是所有线程共享,虚拟机气动的时候建立);栈:存放局部变量(线程创建的时候 被创建);方法区:被虚拟机加载的类信息、常量、静态常量等。 类...

    JAVA内存结构:堆、栈、方法区;

    堆:存放所有 new出来的东西(堆空间是所有线程共享,虚拟机气动的时候建立);栈:存放局部变量(线程创建的时候 被创建);方法区:被虚拟机加载的类信息、常量、静态常量等。

    类加载系统:负责从文件系统或者网络加载class信息,加载的信息存放在方法区。   直接内存:JAVAnio库允许JAVA程序直接内存,从而提高性能,通常直接内存性能高于JAVA堆。读写频繁的场合可能会考虑使用。   本地方法栈:本地方法栈和JAVA栈类似,最大的不同为本地方法栈用于本地方法调用。JAVA虚拟机允许JAVA直接调用本地方法。    垃圾回收系统:是JAVA的核心。       pc寄存器:每个线程私有的空间。JAVA虚拟机为每个线程创建pc寄存器,在任意时刻一个JAVA线程总是在执行一个方法,这个方法被称为当前方法,如果当前方法不是本地方法,pc寄存器总会执行当前正在被执行的指令,如果是本地方法,则pc寄存器值为underfined,寄存器存放当前执行执行环境指针、程序技术器、操作栈指针、计算的变量指针等信息。  执行引擎:虚拟机的核心组件,负责执行虚拟机的字节码,一般先编译成机器码后执行。

    JAVA内存模型:(共享内存模型JMM)定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据时,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

    展开全文
  • 比如本文我们要讨论的JVM内存结构、JAVA内存结构、JAVA内存区域、Java内存模型,这就是几个截然不同的概念,但是很多人容易弄混。 可以这样说,很多高级开发甚至都搞不不清楚JVM内存结构、JAVA内存结构、JAVA内存...
  • Java内存模型-JMM

    万次阅读 2020-09-29 18:08:23
    Java内存模型-JMM 本文是基于CentOS 7.3系统环境,进行Java内存模型的学习和使用 CentOS 7.3 1. Java内存模型 1.1 什么是Java内存模型 Java内存模型(Java Memory Model)本身是一种规范或者一组规则,通过这组...
  • Java与C++之间有- -堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人却想出来。 2.1 概述 对于从事C、C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的“皇帝”...
  • 一篇文章弄懂Java多线程基础和Java内存模型

    万次阅读 多人点赞 2020-04-12 19:07:17
    通过Callable和Future接口创建线程三、Java内存模型概念四、内存间的交互操作五、volatile和synchronized的区别 写在前面:提起多线程大部门同学可能都会皱起眉头不知道多线程到底是什么、什么时候可以用到、用...
  • 全面理解Java内存模型(JMM)及volatile关键字

    万次阅读 多人点赞 2017-06-12 11:25:05
    【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) ... 出自【zejian的博客】...深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深...
  • Java内存模型

    千次阅读 2021-02-13 22:22:59
    Java内存模型 Java内存模型定义了一种多线程访问Java内存的规范。 Java内存模型将内存分为了主内存和工作内存。类的状态也就是类之间共享的变量,是存储在主内存中的,每 次Java线程用到这些主内存中的变量的时候,...
  • Java内存区域与Java内存模型

    千次阅读 2018-02-25 15:09:43
    Java内存区域 Java虚拟机在运行程序时会把其自动管理的内存划分为以上几个区域,每个区域都有其用途以及创建销毁的时机,其中蓝色部分代表的是所有线程共享的数据区域,而绿色部分代表的是每个线程的私有数据区域...
  • Java内存结构是每个java程序员必须掌握理解的,这是Java的核心基础,对我们编写代码特别是并发编程时有很大帮助。由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。 ...
  • Java内存区域怎么划分的?

    万次阅读 2020-09-18 15:12:57
    Java内存区域怎么划分的? 运行时数据区域包含以下五个区域:程序计数器,Java虚拟机栈,本地方法栈,堆,方法区(其中前三个区域各线程私有,相互独立,后面两个区域所有线程共享) 线程私用的部分(Java虚拟机栈,...
  • java内存模型JMM

    万次阅读 2019-01-14 09:21:16
    java内存模型 jmm(java memory model)规范,他规范了java虚拟机与计算机内存如何协调工作 ,他规定了一个线程如何及何时看到其他线程修改过的变量的值,以及在必须时,如何同步的访问共享变量。 jmm内存分配的...
  • JVM:自动内存管理之Java内存区域与内存溢出

    千次阅读 多人点赞 2021-01-07 12:03:22
    一、Java内存区域与内存溢出异常 1、运行时数据区域 运行时数据分为七大块 先来看看JVM内存分布图 1、程序计数器 程序计数器是一个记录着当前线程所执行的字节码的行号指示器。 Java虚拟机中每条线程都有...
  • 希望能够让读者可以对Java内存模型有更加清晰的认识。当然,如有偏颇,欢迎指正。   为什么要有内存模型   在介绍Java内存模型之前,先来看一下到底什么是计算机内存模型,然后再来看Java内存模型在计算机...
  • 【Java并发编程】Java内存模型

    千次阅读 2019-10-24 20:09:03
    文章目录Java内存模型一、JMM解析二、硬件内存架构三、JMM与硬件内存架构的关联四、Java线程与计算机主内存之间的抽象关系五、Java内存模型中同步八种操作 Java内存模型 一、JMM解析 之前写过一篇文章【Java核心技术...
  • Java内存区域划分和内存分配策略

    千次阅读 多人点赞 2020-05-15 12:32:46
    Java内存区域划分和内存分配策略 如果不知道,类的静态变量存储在那? 方法的局部变量存储在那? 赶快收藏 Java内存区域主要可以分为共享内存,堆、方法区和线程私有内存,虚拟机栈、本地方法栈和程序计数器。如下...
  • Java内存管理

    万次阅读 多人点赞 2016-03-08 21:36:06
    不过看了一遍《深入Java虚拟机》再来理解Java内存管理会好很多。接下来一起学习下Java内存管理吧。请注意上图的这个:我们再来复习下进程与线程吧:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,...
  • 比如本文我们要讨论的JVM内存结构、Java内存模型和Java对象模型,这就是三个截然不同的概念,但是很多人容易弄混。 可以这样说,很多高级开发甚至都搞不不清楚JVM内存结构、Java内存模型和Java对象模型这三者的概念...
  • Java内存模型详解

    千次阅读 多人点赞 2020-03-14 07:39:03
    前几天,发了一篇文章,介绍了一下JVM内存结构、Java内存模型以及Java对象模型之间的区别。有很多小伙伴反馈希望可以深入的讲解下每个知识点。Java内存模型,是这三个知识点当中最晦涩难懂的一个,而且涉及到很多...
  • 本来打算自己写个总结,但是看了一篇文章后,自觉很难超出其文,就直接copy过来啦 ,然后本地写了大概笔记,算是...Java垃圾回收概况Java内存区域Java对象的访问方式Java内存分配机制Java GC机制垃圾收集器 Java垃圾
  • Java内存模型(Java Memory Model)

    千次阅读 2020-02-06 20:42:30
    Java内存模型(Java Memory Model,JMM)
  • Java内存管理:在前面的一些文章了解到javac编译的大体过程、Class文件结构、以及JVM字节码指令,下面我们详细了解Java内存区域:先说明JVM规范定义的JVM运行时分配的数据区有哪些,然后分别介绍它们的特点,并指出...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 244,105
精华内容 97,642
关键字:

java内存

java 订阅