美文网首页
Java并发机制的底层原理

Java并发机制的底层原理

作者: CoolTomato | 来源:发表于2019-01-05 23:59 被阅读0次

    Java程序执行:Java代码→Java字节码→字节码被类加载器加载到JVM里,JVM执行字节码→转化为汇编指令在CPU上执行。

    Java中使用的并发机制依赖于JVM的实现和CPU的指令,本章我们来探索下Java并发机制的底层原理。


    volatile的实现原理与应用

    在多线程的并发编程中synchronized和volatile都扮演着重要的角色,volatile是轻量级的synchronized,它在处理器开发中保证了共享变量的“可见性”(可见性:当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。)。

    如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为它不会引起上下文的切换和调度

    Java语言规范第3版中对volatile的定义如下:Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。

    如果一个字段被声明称volatile,Java线程内存模型确保所线程成看到这个变量的值是一致的。

    volatile如何来保证可见性呢?

    Java代码:instance = new Singleton() //instance是volatile变量
    转为汇编代码:0x01a3de1d: movb $0 ✖ 0, 0 ✖ 1104800(%esi);0x01a3de24: lock addl $0 ✖ 0,(%esp);
    有volatile变量修饰的共享变量进行写操作的时候会多处第二行汇编代码,关键在与Lock指令,Lock前缀的指令在多核处理器下会引起两件事情:

    1. 将当前处理器缓存行的数据写回到系统内存。
    2. 这个写回内存的操作会使其它CPU里缓存了该内存地址的数据无效。

    详细说明:为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存后再进行操作,但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作还是会有问题。所以,在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。


    synchronized的实现原理与应用

    在多线程并发编程中synchronized一直是元老级角色,被称为重量级锁。但是,随着Java SE 1.6对其进行各种优化,使得它在有些时候不是那么重。

    利用synchronized实现同步的基础:Java的每一个对象都可以作为锁

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

    Java对象头(暂时不细说此概念)

    synchronized用的锁是存在Java对象头里的。

    锁的升级与对比

    Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有四种状态,级别从低到高一次:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态

    这几个锁会随着竞争情况逐渐升级,锁可以升级但不可以降级。这样做的目的是为了提高获得锁和释放锁的效率。

    优点 缺点 适用场景
    偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 适用于只有一个线程访问同步快场景
    轻量级锁 竞争的线程不会阻塞,提高程序的响应速度 如果始终得不到锁竞争的线程,使用自旋会消耗CPU 追求响应时间,同步块执行速度非常快
    重量级锁 线程竞争不使用自旋,不会消耗CPU 线程阻塞,响应时间缓慢 追求吞吐量,同步块执行速度较长

    原子操作的实现原理(大致聊一聊)

    原子操作:不可被中断的一个或一系列操作。

    处理器如何实现原子操作:

    1. 使用总线锁保证原子性。
    2. 使用缓存锁定保证原子性。

    但是有两种特殊情况下处理器不会使用缓存锁定:

    1. 当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。
    2. 有些处理器不支持缓存锁定。

    Java如何实现原子操作:

    使用循环CAS的方式实现原子操作。

    CAS实现原子操作的三大问题:

    1. ABA问题。
    2. 循环时间长开销大。
    3. 只能保证一个共享变量的原子操作。

    结语:本篇Tomato主要了解下,volatile、synchronized、原子操作的原理,其中原子操作部分说明的不是很详细,因为Tomato自己还没有研究明白,之后会进行更新的。


    最新更新:

    (三)Java并发编程基础—线程简介


    并发编程专题目录

    (一)并发编程的挑战

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

    (三)Java并发编程基础—线程简介


    相关文章

      网友评论

          本文标题:Java并发机制的底层原理

          本文链接:https://www.haomeiwen.com/subject/oxuhrqtx.html