【JVM探险】-02-java内存模型

volatile的内存语义

  1. 可见性 > 对volatile修饰的一个变量的读,总能看到任意一个线程对这个变量的最后写入。
  2. 操作是强制把写本地写缓冲区的内容写入主内存,同时使其他工作内存中的缓存行失效,读操作时从主内存读取。
  3. 禁止指令重排序
  4. 通过在cpu指令间插入内存屏障指令,用来禁止处理器指令发生重排序

           jvm volatile指令重排序规则:

  1.     第一个操作是volatile读,不管第二个操作是什么都不能重排
  2.     第二个操作是volatile写,不管第一个操作是什么都不能重排
  3.     第一个操作是volatile写,且第二个操作是volatile读,指令不重排

           原子性

                1、对任意单个volatile变量的读/写具有原子性

 

          volatile读的内存语义

                1、当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

 

 

Synchronized锁实现原理

synchroinzed是通过对象内部的monitor来实现的,而monitor的实现底层依赖于操作系统的Mutex Lock来实现,操作系统线程间的切换涉及到用户态和内核态的转变,成本高。

操作系统内核态和用户态 是对指令特权级别的两种分类。用户态的指令不能访问到内核态,所以要调用系统的内核态指令必须切换到内核态去执行。执行完后再切回用户态,以此来保护高危险级的内核态指令代码。

切换到内核态有几种方法

1、主动调用,即用户态主动要求调用内核态的相关指令代码。

2、线程中断,cpu接收到中断信号时,如果正在执行的指令是用户态指令,则会暂停当前线程,转而去执行内核态的中断指令,这个过程就发生了从用户态到内核态的转变。

3、某些异常发生时,cpu要从用户态切换到内核态执行相关异常处理指令。

 

 

Java内存模型的抽象结构

volatile的内存语义中的内存可见性,来源于java内存模型中的定义,存在堆内存中的共享变量存储在主内存中,而各个线程其本身都有一个私有的本地内存,这其实是JVM的一个抽象概念,本地内存其实是缓存区、寄存器。本地内存中会存储主内存中共享变量的一个副本,线程操作共享变量时会操作本地内存中的该变量副本,并合适的时间同步主内存,因为共享变量在各个线程本地内存中差异不同步,这就是造成内存可见性存在的原因。

 

 

1、线程A从主内存中读取共享变量,并在本地内存中创建副本。

2、线程A更新本地内存中的共享变量副本,并刷新到主内存中去。

3、线程B从主内存中读取线程A更新过的共享变量。

4、jvm通过控制本地内存变量副本强制刷到主内存,并使其他线程的缓存行失效从而去强制读取主内存,而达到内存可见性的保证。

正常情况下,2步骤不会立即刷新到主内存,因为现代处理器都有仅对其自己可见的写缓冲区,用来临时保存向内存写入的数据,以此来减少频繁写入导致的处理器停顿。由于写缓存区的存在,现代处理器都允许对写-读操作进行重排序。通过插入StoreLoad内存屏障,可以禁止此类型的处理器重排序,并将写缓冲区的数据全部刷新到主内存。

 

happens-before

前一个操作的执行结果对后一个操作可见,且前一个操作顺序排在后一个操作之前。

对一个锁的解锁,happens-before于随后对这个锁的加锁。

对一个volatile变量的写,happens-before于任意后续对这个volatile变量域的读。

   

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值