(二)Java并发机制底层实现原理

1. volatile的原理

volatile,轻量级的synchronized,保证的是一个线程修改后,另一个线程立马可见的可见性,不会引起上下文切换。

底层实现
volatile Singleton instance = new Singleton()

JIT编译器生成的指令代码如下:

0x01a3deld: 
	mov $0x0,0x1104800(%esi);
	0x01a3de24: lock addl $0x0, (%esp);

有volatile修饰的变量进行写操作时会多出第二行汇编代码,lock指令在多核处理器下产生两个结果:

  1. 将当前处理器缓存的数据写回系统内存;
  2. 写回内存操作使其它CPU里缓存了该内存地址的数据无效,导致重新从系统内存读取;

以上操作导致了该变量的修改在不同线程之间是可见的。

2. synchronized的原理与应用

synchronized是重量级锁,随着Java1.6进行优化以后,引入了偏向锁、轻量级锁,以及锁的存储结构和升级过程。
synchronized锁有三种形式:

  1. 普通同步方法,锁是当前实例;
  2. 静态同步方法,锁是当前Class对象;
  3. 同步方法块,锁是括号里配置的对象;

synchronized是由JVM实现的,代码块同步是通过编译后在代码开始和结束(包括异常)位置分别使用monitorenter和monitorexit指令来实现的,同步方法则是方法属性里加入同步标记来实现。任何对象都有一个monitor与之关联。

1)Java对象头

synchronized使用的锁是存在Java对象头里的,对象为数组类型,则虚拟机使用3个字宽(32位虚拟机一个字宽为32位,即4个字节),非数组类型使用2个字宽存储对象头。

长度内容说明
32/64 bitMark Word对象的hashCode、锁信息
32/64 bitClass Metadata Address对象类型数据的地址
32/64 bitArray Length数组长度(数组类型专有)

MarkWord的存储结构:

锁状态25bit4bit1bit 是否偏向锁2bit 锁标志位
无锁状态对象HashCode对象分代年龄001

Mark Word的几种状态

2)锁升级

Java 1.6为了减少锁获取和释放带来的性能损耗,引入了“偏向锁”和“轻量级锁”,级别从低到高:无锁->偏向锁->轻量级锁->重量及锁。锁可以升级,但不能降级。

  • . 偏向锁
    一个线程访问同步块时,会在对象头和栈帧的锁记录里存储当前线程ID,以后该线程在进入和退出时不需要CAS来加解锁,只需简单测试MarkWord里是否有偏向锁指向自己。
    在这里插入图片描述
  • 轻量级锁
    加锁:执行同步块之前,JVM先在当前线程栈帧中创“锁记录”空间,并将MarkWord复制到“锁记录”中,然后尝试使用CAS将对象头中MarkWord替换为指向“锁记录”的指针,成功则获取锁,失败则使用“自旋”来获取锁。
    解锁:使用原子操作将“锁记录”替换回对象头,成功则表示没有竞争,失败表示存在竞争,锁膨胀为重量级锁。
3. 原子操作实现原理
1)处理器如何实现原子锁
  • 总线锁定
    使用处理器提供的Lock#信号,一个处理器输出此信号时,其他处理器请求将被阻塞。锁定开销较大。
  • 缓存锁定
    内存区域被缓存在处理器的缓存行中,Lock期间被锁定,执行回写内存时(缓存一致性会阻止同时修改由两个处理器缓存的内存区),会使其他缓存了该内存区的数据无效,导致重新读取内存。
2) Java中原子操作

实现方式:

  • 锁:如之前内容所述,获取锁的线程才能操作指定共享的内存区域;
  • 循环CAS:使用了处理器提供的CMPXCHG指令;

循环CAS存在的问题:
1. ABA问题,解决方法:版本号机制,每次操作版本号+1;
2. 循环时间长,开销大;
3. 只能保证一个变量的原子操作;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值