JAVA并发编程-底层机制和原理

代码执行过程

java代码首先会被javac编译成java字节码,然后通过class loader(类加载器)加载到JVM里,然后通过执行引擎将字节码转为汇编指令在CPU上执行。

volatile

它保证了在多核CPU中,共享变量的可见性。它比synchronized更轻量,在JAVA语言规范第三版中有明确说明,允许线程访问共享变量,但线程应该有排它锁来单独获取这个变量。volatile在某些情况比排它锁更方便,如果某个变量被声明为volatile,那么JVM能保证所有线程看到的该变量值一样。

synchronized

同步锁,在JDK1.6之后它已不再是重量级锁,而是引入了偏向锁,轻量级锁。

  • 普通方法,锁的对象是当前实例
  • 静态方法,锁的对象是该类
  • 方法块,括号内的对象
    它加锁是根据进入和退出monitor来确定的,也就是说在我们编译的字节码文件中,对应加了synchronized的对象,会在该同步代码块之前加上monitorenter,结束后会加上monitorexit。

JAVA对象头

如果对象是数组,则占用3个字宽,在32位操作系统中,占用32+32+32位,在64位中占用64+64+32位。
他们分别是:

长度内容说明
32/64Mark Word存储对象的Hashcode和锁信息
32/64Class MataData Address存储到对象的数据指针
32Array Length数组长度
Mark Word
锁状态25bit4bit1bit2bit
23bit 2bit 是否偏向锁标志位
无锁对象hashcode对象分代年龄001
偏向线程IDEpoch对象分代年龄101
轻量栈中锁记录的指针00
重量指向互斥量的指针10
GC标记11

由上面表格可以看出,无锁和偏向的锁标志位都是01。偏向锁一般用于锁竞争小,或者无竞争的情况,如果竞争强烈,偏向锁会影响性能,因为它本身不会释放锁,而是CAS获取偏向锁失败时,偏向锁会在全局安全点时,获得该偏向锁的线程挂起,然后判断该线程是否还在方法体内,如果在那么升级为轻量锁,如果已经退出则释放锁,变成无锁状态,这需要时间开销。

轻量级锁的获取

线程在执行同步代码块之前,JVM会在当前线程的栈帧中创建保存锁记录的空间,并将对象头中的Mark Word复制到锁记录中,然后尝试使用CAS将MarkWord替换为创建的锁记录空间的指针。如果成功获得锁,偏亮升级为轻量。如果失败,自旋获取锁。

轻量级锁解锁

用CAS操作将Mark Word替换回原来的对象头,如果失败锁膨胀为重量锁。

CAS操作

在java中可以通过锁或者循环CAS来实现原子性操作。
循环CAS就是一直尝试,知道成功。CAS的操作其实就是比较,替换。

CAS的三大问题
  1. ABA问题。意思就是CAS会检测值有没有发生变化,如果一个值从A-B-A值本身没有变化,但实际是由变化过程的,但是CAS会认为没有变化。要感知这种变化,可以再值前面加上版本号,比如:1A-2B-3A
  2. 循环时间长。意思就是自旋CAS长时间不成功,会造成CPU开销很大
  3. 只能保证一个共享变量。因为是比较替换,所以只能保证一个共享变量。
锁保证原子操作

也就是说锁可以保证只有获得锁的线程才能够操作锁定的区域。但是除了偏向锁,JVM实现锁的方式都用了循环CAS实现。到底是谁成就了谁?

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值