zl程序教程

您现在的位置是:首页 >  Java

当前栏目

Java并发机制的底层实现原理

2023-02-18 16:34:06 时间

转载请以链接形式标明出处: 本文出自:103style的博客

Java代码 编译之后 得到 Java字节码,被 类加载器加载到JVM中,最终 转化为汇编指令。

Java并发编程的艺术笔记


volatile

volatile是轻量级的synchronized,被volatile修饰的变量,在一个线程能读到这个变量被另一个线程修改之后的值。 volatile不会引起线程上下文切换和调度。

volatile的两条实现原则

  • Lock前缀指令会引起处理器缓存回写到内存.
  • 一个处理器的缓存回写到内存会导致其他处理器的缓存无效.

synchronized实现同步

  • 对于 普通同步方法,锁是 当前实例对象。
  • 对于 静态同步方法,锁是 当前类的Class对象。
  • 对于 同步方法块,锁是 Synchonized括号里配置的对象。

Synchonized在JVM里的实现原理

JVM 基于进入和退出Monitor对象来实现方法同步和代码块同步。

  • 代码块同步是使用monitorentermonitorexit指令实现的
  • 方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用monitorentermonitorexit指令来实现。

monitorenter指令是在编译后插入到同步代码块的开始位置,monitorexit是插入到方法结束处和异常处。 JVM要保证每个monitorenter必须有对应的monitorexit与之配对。 任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

Java对象头

synchronized用的锁是存在Java对象头里的。在32位 虚拟机中,1字宽 等于4字节,即32bit

  • 数组类型,虚拟机用3个字宽存储对象头。
  • 非数组类型,虚拟机用2字宽存储对象头。

锁的4种状态

级别从低到高依次是:

  • 无锁状态
  • 偏向锁状态
  • 轻量级锁状态
  • 重量级锁状态

Java中的锁介绍

原子操作的实现原理

原子(atomic)本意是“不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为“不可被中断的一个或一系列操作”。

  • 处理器如何实现原子操作
    • 使用总线锁保证原子性:所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。
    • 使用缓存锁保证原子性。 以下两种情况不会使用缓存锁:
      • 当处理器不支持缓存锁定。
      • 当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。
  • Java如何实现原子操作
    • 使用循环CAS实现原子操作Java中的12个原子操作类介绍
    • CAS实现原子操作的三大问题:
      • ABA问题:因为CAS需要在操作值的时候,检查值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。 ABA问题的解决思路就是 使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号1,那么A→B→A就会变成1A→2B→3A。 原子操作类AtomicStampedReferencecompareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
      • 循环时间长开销大:自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。
      • 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。还有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。
    • 使用锁机制实现原子操作 锁机制保证了只有获得锁的线程才能够操作锁定的内存区域。