Java并发编程的艺术学习笔记(二)

238 阅读4分钟

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

1. volatile

1.1 volatile的作用

volatile是轻量级的synchronize,它在多处理器 开发中保证了共享变量的“可见性”。所谓可见性是指当一个线程修改共享变量时,其它线程能立即读取到该已被修改的变量值。volatile的开销更低,且不会引起线程上下文的切换。

1.2 volatile的定义:

如果一个变量被声明为volatile, Java内存模型确保所有线程看到该变量的值是一致的。

1.3 volatile的实现原理

有volatile修饰符的变量在进行写操作时,其编译后的汇编指令有一行带有Lock前缀的指令。该指令在多处理器下会引发两件事:

1)将当前处理器缓存行的数据写会到主内存中去。

2)其他处理器里缓存了该内存地址的数据设置为无效状态。

2. volatile的特性

●可见性。当对volatile变量进行写操作时,其它线程能立即看到该变量被写入或修改后的最新值。

●原子性。对任意单个volatile变量的读写具有原子性。即volatile变量在进行读写时不能被中断,但volatile变量++这种操作则不具有原子性。

2.1 可见性

●可见性的存在意义(为什么需要可见性):java线程之间的共享变量是存储在主内存中的,而每个线程都拥有自己私有的本地内存,本地内存中存储了该线程所读/写的共享变量的副本。当线程对共享变量进行修改时,被改变的是本地内存中的副本,而主内存中的共享变量并没有发生改变。这样当其他线程在主内存中读取该共享变量时,获取到的是没有刷新的旧值。这时就需要内存的可见性来解决这个问题。

●volatile变量保证内存可见性的方式(如何保证内存可见性):

  • ●当对一个volatile变量进行读操作时,会把主内存中的共享变量读取到线程的本地内存中。
  • ●当对一个volatile变量进行写操作时,会把线程本地内存中的共享变量刷新到主内存中。

●volatile变量进行读/写操作的一些注意事项:

  • ●当对某个volatile变量进行读操作时,主内存中所有共享变量都将被一同读进线程的本地内存。
  • ●当对某个volatile变量进行写操作时,线程本地内存中的所有共享变量都将被写进主内存中。

2.2 原子性

●处理器实现原子操作的机制

  • ●通过总线锁保证原子性。总线锁就是使用处理器提供的Lock #信号。当一个处理器在总线上输出此信号时,其他处理器的请求将会被阻塞,此时共享内存将会被当前处理器独占。但总线锁的开销比较大,因为其他处理器和内存的通信被锁住了。
  • ●通过缓存锁定保证原子性。缓存锁定是指内存区域如果被缓存在处理器的缓存行中,当它执行锁操作写回到内存中时,处理器会修改处理器内部缓存中的内存地址,并且处理器的缓存一致性机制将保证此操作的原子性(缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据),当其他处理器回写已被锁定的缓存行数据时,会使缓存行无效。(注:个人认为《java并发编程的艺术》书中对此段话的描述比较模糊,本人也不是十分理解)。

●Java中实现原子操作的方式

  • ●使用锁
  • ●循环CAS

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

  • ●ABA问题
  • ●循环时间长导致开销大
  • ●只能保证一个共享变量的原子性

2.3 重排序

重排序是编译器为了提高程序的执行效率从而对代码的执行顺序进行调整。当变量被声明为volatile时,其重排序规则不同于普通变量,规则如下:

●对volatile变量写操作时,不能与先前的代码重排序。

●对volatile变量读操作时,不能与其后的代码重排序。

●前为volatile写,后为volatile读时,不能重排序。