volatile 关键字(详细讲解)

我认为volatile 关键字有两个作用:

  • 第一个,它是可以去保证多线程环境下对于共享变量的可见性。
  • 第二个,是可以通过增加内存屏障去防止多个指令之间的一个重排序。

我理解的可见性呢,是指当一个线程对于共享变量的修改,其他线程可以立刻看到修改之后的一个值。其实这个可见性问题我认为本质上是由几方面造成的:

  • 首先是CPU层面的告诉缓存,在CPU里面设计了三级缓存去解决CPU运算效率和内存IO效率的问题,但是他也带来了就是缓存一致性问题,而在多线程并行执行的情况下,缓存一致性就会导致可见性问题。所以对于增加了volatile关键字的一个修饰的共享变量,JVM虚拟机会自动增加一个lock的汇编指令,而这个指令会去根据不同的CPU型号去自动添加总线锁或缓存锁。我简单说一下这两种锁:①总线锁,它锁定的是CPU的前端总线,从而去导致在同一时刻只能有一个线程和内存通信,这样就避免了多线程并发造成的可见性问题。②缓存锁,是对总线锁的一个优化,因为总线锁导致CPU的使用效率大幅度下降,所以缓存锁只针对CPU三级缓存中的目标数据去加锁,而缓存锁是使用MESI缓存一致性协议来是实现的。

第二个是指令重排序,所谓重排序就是说指令在编写的顺序和执行的顺序是不一致的,从而在多线程环境下导致可见性问题,指令重排序本质上是一种性能优化的手段,它来自于几个方面:

  • 首先是CPU层面,针对于MESI协议的更进一步的优化去提升CPU的一个利用率所以他引入了一个叫StoreBuffer的一个机制,而这种优化机制会导致CPU的乱序执行,当然为了避免这样的问题,CPU提供了内存屏障指令,上层应用可以在合适的地方去插入内存屏障去避免CPU指令重排序问题。
  • 第二个是编译器的层面的优化,编译器在编译的过程中在不改变单线程语义和程序正确的前提下对指令进行合理的重排序,从而去优化整体的一个性能。所以啊,对于共享变量增加了volatile 关键字,那么编译器层面就不会去触发编译器的优化,同时在JVM里面,他会插入内存屏障指令来去避免重排序问题,当然除了volatile 关键字以外,从JDLK1.5开始,JMM就使用了一种Happens-Before的模型去描述多线程之间可见性的一个关系,也就是说,如果两个操作之间具备Happens-Before关系,那么意味着两个操作具备可见性一个关系,不需要再额外考虑增加volatile关键字来提供可见性的一个保障。

synchronized 关键字和 volatile 关键字的区别?
synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!

  • volatile 关键字是线程同步的轻量级实现,所以**volatile性能肯定比synchronized关键字要好**。但是**volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块**。
  • volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
  • volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。

你可能感兴趣的:(并发多线程,volatile,并发,多线程,java)