并发之volatile底层原理

Posted awkflf11

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发之volatile底层原理相关的知识,希望对你有一定的参考价值。

 

12.Java多线程-java.util.concurrent.atomic包原理解读

11.volatile底层实现原理

===================

 

 

12.Java多线程-java.util.concurrent.atomic包原理解读

多线程基本类型
AtomicReference
Atomic*
Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类,但Atomic的线程安全是如何来实现的呢?
1、硬件同步策略
现在的处理器都支持多重处理,当然也包含多个处理器共享外围设备和内存,同时,加强了指令集以支持一些多处理的特殊需求。
特别是几乎所有的处理器都可以将其他处理器阻塞以便更新共享变量
2、Compare and swap(CAS)
当前的处理器基本都支持CAS,只不过每个厂家所实现的算法并不一样罢了,每一个CAS操作过程都包含三个运算符:一个内存地址V,
一个期望的值A和一个新值B,操作的时候如果这个地址上存放的值等于这个期望的值A,则将地址上的值赋为新值B,否则不做任何操作。
CAS的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少。我们来看一个例子,
解释CAS的实现过程(并非真实的CAS实现)

3、实现的核心源码:
 /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
    public final boolean compareAndSet(int expect, int update) {
        //使用unsafe的native方法,实现高效的硬件级别CAS  
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
他比直接使用传统的java锁机制(阻塞的)有什么好处?
最大的好处就是可以避免多线程的优先级倒置和死锁情况的发生,当然高并发下的性能提升也是很重要的

4、CAS线程安全
说了半天,我们要回归到最原始的问题了:这样怎么实现线程安全呢?请大家自己先考虑一下这个问题,其实我们在语言层面是没有做任何同步的操作的,
大家也可以看到源码没有任何锁加在上面,可它为什么是线程安全的呢?这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,
利用CPU的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,
只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!

5总结
虽然基于CAS的线程安全机制很好很高效,但要说的是,并非所有线程安全都可以用这样的方法来实现,这只适合一些粒度比较小,
如计数器这样的需求用起来才有效,否则也不会有锁的存在了

11.volatile底层实现原理

定义

java编程语言允许线程访问共享变量,为了确保共享变量能够被准确和一致的更新,线程应该通过排他锁获得这个变量。java提供了volatile,在某些情况下比锁更加方便。如果一个字段被声明成volatile,java线程内存模型确保所有线程看到的这个变量的值是一致的。

汇编代码

使用命令获得汇编代码

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output
Loaded disassembler from /Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home/jre/lib/hsdis-amd64.dylib
Decoding compiled method 0x0000000110da4b50:
Code:
[Disassembling for mach=‘i386:x86-64‘]
[Entry Point]
[Constants]
  # {method} {0x000000010f163000} ‘hashCode‘ ‘()I‘ in ‘java/lang/String‘
  #           [sp+0x40]  (sp of caller)
  0x0000000110da4cc0: mov    0x8(%rsi),%r10d
  0x0000000110da4cc4: shl    $0x3,%r10
  0x0000000110da4cc8: cmp    %rax,%r10
  0x0000000110da4ccb: jne    0x0000000110ceae20  ;   {runtime_call}
  0x0000000110da4cd1: data32 data32 nopw 0x0(%rax,%rax,1)
  0x0000000110da4cdc: data32 data32 xchg %ax,%ax
[Verified Entry Point]
  0x0000000110da4ce0: mov    %eax,-0x14000(%rsp)
  0x0000000110da4ce7: push   %rbp
  0x0000000110da4ce8: sub    $0x30,%rsp
  ......

mac系统下使用此命令的前提是下载hsdis-amd64.dylib,并将其放入到jdk的jre下的lib目录下

实现原理

通过利用工具获得class文件的汇编代码,会发现,标有volatile的变量在进行写操作时,会在前面加上lock质量前缀。

技术分享图片

而lock指令前缀会做如下两件事

  1. 将当前处理器缓存行的数据写回到内存。lock指令前缀在执行指令的期间,会产生一个lock信号,lock信号会保证在该信号期间会独占任何共享内存。lock信号一般不锁总线,而是锁缓存。因为锁总线的开销会很大。

  2. 将缓存行的数据写回到内存的操作会使得其他CPU缓存了该地址的数据无效。 

 





































以上是关于并发之volatile底层原理的主要内容,如果未能解决你的问题,请参考以下文章

并发编程艺术-锁类型以及底层原理

#yyds干货盘点#Java并发机制的底层实现原理

第二章 并发机制的底层实现原理

JAVA并发编程:java并发机制的底层实现原理

JAVA并发编程:java并发机制的底层实现原理

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