Java并发:Java内存模型(一)

深入探讨Java线程间的通信机制,揭示Java内存模型(JMM)如何确保多线程环境下的内存可见性。涵盖JMM基础、重排序规则及happens-before原则,帮助理解Java并发编程的核心。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

      Java线程之的通信程序完全透明,内存可问题很容易困Java程序,本系列将揭开Java内存模型神秘的面。本系列大致分为4个部分:

  1. Java内存模型的,主要介内存模型关的基本概念
  2. Java内存模型中的序一致性,主要介重排序与序一致性内存模型;
  3. 同步,主要介3个同步原synchronizedvolatilefinal)的内存语义及重排序规则理器中的实现
  4. Java内存模型的设计,主要介Java内存模型的设计原理,及其与理器内存模型序一致性内存模型的关系。

1. Java内存模型的基础  

 1.1 并发编程模型的两个关键问题

        在并发编程中,需要理两个关键问题线程之如何通信线程之如何同步里的线程是指并发执行的活动实体)。

  (1)、通信是指线程之以何种机制来交信息。在命令式中,线程之的通信机制有两种:共享内存和消息传递

        在共享内存的并发模型里,线程之共享程序的公共状,通-内存中的公共状式通信。在消息传递的并模型里,线程之没有公共状线程之过发送消息来行通信。

  (2)、同步是指程序中用于控制不同线操作生相对顺序的机制。在共享内存并模型 里,同步是行的。程序须显式指定某个方法或某段代需要在线程之互斥行。 在消息传递的并模型里,由于消息的送必在消息的接收之前,因此同步是行的。

       Java的并采用的是共享内存模型,Java线程之的通信行,整个通信程序完全透明。如果写多线程程序的Java程序不理解行的线程之通信的工作机制,很可能会遇到各种奇怪的内存可问题

   

 1.2 Java内存模型的抽象结构

      Java中,所有例域、静域和数元素都存在堆内存中,堆内存在线程之共享(本章用共享术语代指例域,静域和数元素)。局部量(Local Variables),方法定参数(Java范称之Formal Method Parameters)和异常理器参数(Exception Handler Parameters)不会在线程之共享,它不会有内存可问题,也不受内存模型的影响。

       Java线程之的通信由Java内存模型(本文JMM)控制,JMM决定一个线共享量的写入何时对另一个线程可。从抽象的角度来看,JMM线程和主内存之的抽象关系:线程之的共享量存在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存该线程以/写共享量的副本。本地内存是JMM一个抽象概念,并不真存在。它涵盖了存、写冲区、寄存器以及其他的硬件和编译化。Java内存模型的抽象示意如3-1所示。

                            

从图3-1来看,如果线A线B要通信的,必经历下面2个步

1线A把本地内存A中更新的共享量刷新到主内存中去。

2线B到主内存中去线A之前已更新的共享量。

下面通示意见图3-2)来两个步

                         

 

       如3-2所示,本地内存A和本地内存B由主内存中共享x的副本。假初始3个内存中的x0线A,把更新后的x(假设值为1临时存放在自己的本地内存A中。当线A线B需要通信线A首先会把自己本地内存中修改后的x刷新到主内存中,此时主内存中的x值变为1。随后,线B到主内存中去线A更新后的x,此时线程B的本地内存的x变为1

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

 

1.3 从源代码到指令序列的重排序

       在执行程序了提高性能,编译器和理器常常会指令做重排序。重排序分3类型。

1)编译化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排句 的执序。

2)指令并行的重排序。理器采用了指令并行技Instruction-Level Parallelism,ILP)来将多条指令重叠行。如果不存在数据依性,理器可以改变语对应机器指令的执序。

3)内存系的重排序。由于理器使用存和/冲区,使得加和存操作看上去可能是在乱序执行。

Java源代到最终实际执行的指令序列,会分别经历下面3种重排序,如3-3所示。

 

     上述的1属于编译器重排序,23属于理器重排序。些重排序可能会致多线程程序出现内存可问题编译器,JMM编译器重排序规则会禁止特定型的编译器重排序(不是所有的编译器重排序都要禁止)。理器重排序,JMM理器重排序规则会要求Java编译器在生成指令序列,插入特定型的内存屏障(Memory BarriersIntel称之为 Memory Fence)指令,通内存屏障指令来禁止特定型的理器重排序。

      JMM属于的内存模型,它确保在不同的编译器和不同的理器平台之上,通止特定型的编译器重排序和理器重排序,程序提供一致的内存可性保

 

1.4 并发编程模型的分类

       代的理器使用冲区临时保存向内存写入的数据。写冲区可以保指令流水线运行,它可以避免由于理器停下来等待向内存写入数据而生的延。同,通理的方式刷新写冲区,以及合并写冲区中同一内存地址的多次写,减少内存线的占用。然写冲区有么多好,但每个理器上的写冲区,仅仅对它所在的理器个特性会内存操作的生重要的影响:理器内存的/写操作的序,不一定与内存实际发生的/写操作序一致!了具体明,看下面的表3-1

   设处理器A理器B按程序的序并行行内存访问,最可能得到x=y=0果。具体的原因如3-4所示。

 

理器A理器B可以同把共享量写入自己的写冲区(A1B1),然后从内存

取另一个共享量(A2B2),最后才把自己写存区中保存的数据刷新到内存中(A3

B3)。当以,程序就可以得到x=y=0果。

                      

 

       这理器A理器B可以同把共享量写入自己的写冲区(A1B1),然后从内存中读取另一个共享量(A2B2),最后才把自己写存区中保存的数据刷新到内存中(A3,B3)。当以,程序就可以得到x=y=0果。

        从内存操作实际发生的序来看,直到理器AA3来刷新自己的写存区,写操作A1才算真正行了。理器A行内存操作的A1→A2,但内存操作实际发生的顺序却是A2→A1。此理器A的内存操作序被重排序了(理器B的情况和理器A,这里就不述了)。

        这里的关是,由于写冲区仅对自己的理器可,它会理器行内存操作的 顺序可能会与内存实际的操作序不一致。由于代的理器都会使用写冲区,因此代的理器都会允许对-操作行重排序。

 

1.5 happens-before简介

       在JMM中,如果一个操作执行的果需要另一个操作可,那么两个操作之要存在happens-before关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之

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

   ·程序规则:一个线程中的每个操作,happens-before该线程中的任意后操作。

   ·监视锁规则一个的解happens-before于随后对这的加

   ·volatile规则一个volatile域的写,happens-before于任意后续对这volatile域的读。

   ·传递性:如果A happens-before B,且B happens-before C,那么A happens-before C

注意 两个操作之具有happens-before关系,并不意味着前一个操作必要在后一个操作之前执行!happens-before仅仅要求前一个操作(行的果)后一个操作可,且前一个操作按顺序排在第二个操作之前(the first is visible to and ordered before the second)。happens-before的定很微妙,后文会具体happens-before什么要么定

 

happens-beforeJMM的关系如3-5所示。

                 

        如图3-5所示,一个happens-before规则对应于一个或多个编译器和理器重排序规则对于Java程序happens-before规则简单易懂,它避免Java程序员为了理解JMM提供的内存可见性保而去学的重排序规则以及规则的具体实现方法。

 本文内容来自《java并发编程的艺术》一书。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值