java虚拟机-java内存模型与线程

Java内存模型确保跨平台的并发一致性,定义了主内存和工作内存的交互规则。volatile关键字提供轻量级同步,保证可见性和禁止指令重排,但不保证原子性。long和double的读写可能非原子,现代虚拟机通常会视为原子操作。此外,内存模型的三大特性——原子性、可见性和有序性,通过同步原语如synchronized保证线程间操作的有序性。
摘要由CSDN通过智能技术生成

java内存模型

java虚拟机规范试图定义一种java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,实现java程序在各平台下都能达到一致的并发效果。c与c++直接使用物理硬件(操作系统的内存模型),因此需要针对不同平台编写程序。

主内存和工作内存

java内存模型的主要目标是定义程序中各变量的访问规则,即虚拟机中将变量存储到内存和从内存中取出变量的底层细节。这里的变量包括实例字段静态字段,和构成数组对象的元素,因为他们在多线程下,会存在竞争不包括线程私有的局部变量和方法参数

java内存模型规定 所有变量都储存在 主内存,除此之外,每个线程还有自己的工作内存。线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对于变量的所有操作都在工作内存,不能直接读写主内存。线程直接也不可以访问各自的工作内存的变量,变量值的传递通过主内存完成。
在这里插入图片描述
这里讲的内存模型和java内存区域中的堆栈不是一个层次的内存划分。

内存间的交互

主内存和工作内存的交互,通过下面八种方式来完成。

  • lock(锁定):作用于主内存变量,把变量标识为线程独占状态。
  • unlock(解锁):作用于主内存变量,把锁定的变量释放
  • read(读取):作用于主内存变量,把变量的值(只是值,不是变量)从主内存传输到线程的工作内存,以便load操作使用
  • load(载入):作用于工作内存变量,把read操作得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存变量,把变量的值传递给执行引擎。
  • assign(赋值):作用于工作内存变量,把从执行引擎收到的值赋值给工作内存的变量。
  • store(存储):作用于工作内存。把工作内存中的变量传送给主内存,以便随后write使用
  • write(写入):作用于主内存变量,把store操作从工作内存得到的变量值放到主内存变量中。

上述八种操作必须满足一些规则,这里就不列举了,感兴趣自行查阅。

volatitle关键字

volatitle关键字是java虚拟机提供的最轻量级的同步机制。它的性能和普通变量相比,读的速度几乎一样,但写的速度会慢一些,因为需要在本地代码中插入一些内存屏障。
java内存模型对volatitle专门定义了一些特殊的访问规则:可见性禁止指令重排

可见性

当一个变量值被修改,其他线程可以立即得知仅仅是得知(通过每次使用前都从主内存刷新最新值,每次赋值后,都写入主内存实现)。但是,这不代表线程安全。只能保证可见,就是我们的线程的知道它改变了,但我们对变量执行其他的操作的时候,这个值可能已经被其他线程修改了。类似 i++的整个过程分为步骤1: 看到值,步骤2:修改值,通过volatitle关键字,我们可以保证步骤1总是正确,但是步骤2不一定正确,因为这个过程是非原子的。该关键字不能保证整个过程为原子操作,如果需要整个过程是原子操作,就使用sycchozied。
所以使用volatitle关键字需要满足两个规则:
(1)运算结果不依赖当前值,或者保证只有单一线程修改变量值。
(2)变量不需要与其他状态变量共同参与不变约束。

禁止指令重排

java内存模型中,线程内表现为串行语义,这就是说,线程内,赋值都能得到正确结果,语义串行,但代码执行顺序不一定保证,可能被优化。这在单一线程中无关紧要,但是在多线程中,就可能造成不良影响。例如,A线程 我们代码顺序是 先实例化一个对象,然后打开开关。在B线程中检测到开关打开,然后引用A线程中实例化的对象做一些事情。如果进行了指令重排,可能我们B线程就会引用到一个空对象。

long和double型变量的特殊规则

java内存模型对lock等八大操作都要求具有原子性,但是对于64位的数据类型(long和double),允许虚拟机将没有被volatitle修饰的64位数据,读写划分为两次32位操作,即 read ,load,assgin,write可以不保证原子性,这就是非原子协定。这在多线程过程中,就可能存在读取到半个变量的情况,就是第一次32位操作后就被其他线程读取,这种情况是很罕见的。现在商业虚拟机几乎都选择把64位数据的读写操作当做原子操作看待,所以对于double和long,没有必要加volatitle修饰。

原子性,可见性,有序性

java内存模型的三个特征,就是原子性,可见性,有序性。

  • 原子性:java内存模型保证原子性变量操作(包括read等六个操作),我们大致可以认为基本数据类型的访问读写是原子性的(非原子协定暂时不考虑),更大访问的原则性保证由lock和unlock提供,在java代码的体现就是synchronized块。
  • 可见性:当一个线程修改了共享变量的值,其他线程能够立即知道。java内存模型通过变量修改后同步回主内存,变量读取前从主内存刷新变量值的方式实现可见性。普通变量和volatitle变量都是如此,但是volatitle变量特殊的机制,保证新值能立即同步每次使用前立即从主内存刷新
  • 有序性:本线程内观察,所有操作都是有序的(线程内串行语义);在一个线程观察另外一个线程,所有都是无序的(指令重排现象工作内存与主内存同步延迟现象)。

java语言提供volatile关键字synchonized关键字保证线程之间操作的有序性,volatile本身禁止指令重排,synchonized通过“在同一时刻,只允许一条线程对其进行lock操作”获得,这就保证了两个同步块,只能串行的进入。

先行先发生原则

时间上的先发生和先行先发生并不相互影响。即时一个操作时间上,被处理器先处理,但不影响它在时间上后处理的操作之后执行,所以二者不能相互推导,这就是指令重排。
一些天然的先行发生关系:

  • 程序次序规则
  • 管程锁定规则
  • volatile规则
  • 线程启动规则
  • 线程终止规则
  • 线程中断规则
  • 对象终结规则
  • 传递性
    具体内容可以自行查阅。
  • 15
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值