在上文介绍了Volatile在编程语言层面支持并发编程的内存可见性,cpu侧是如何实现的。
下面是没有使用Volatile的代码示例
如上代码所示定义的counter共享计数器对象,并没有使用volatile ,synchronize这样的关键,在对共享对象进行多线程读写的情况下,是无法保证一致性性和准确性的。因为在读写操作时,先是对本地缓存进行操作的。如下图所示:
如上图所示,cpu cache数据与主内存数据是不一致的,Thread2线程没有看到一个变量的最新值,因为它还没有被另一个线程写回主内存,这个问题被称为“可见性”问题。一个线程的更新对其他线程不可见。但,上面代码中的counter++,这是个复合操作,只能保证可见性,却不能保证原子性。
Volatile变量自身特性:
1. 可见性:对一个Volatile变量的读,总是能看到任意线程对这个Volatile变量最后的写入。如线程1写counter变量,线程2读取时,对于线程1操作该变量的前后都是可见的。
2. 原子性:对任意单个变量的读写具有原子性,但像counter++这种复合操作不具有原子性。
看到这里有没有注意到上面的代码有问题,也就是这个counter++ 操作,这个复合操作有读取、计算、写入,无法保证原子性,所以在主内存中的数据会出现覆盖的情况,数据不准确,根据实际情况选择使用。
完整的volatile可见性保证小技巧
示例代码如下:
可见性保证遵循以下规则:
如果线程A写入一个volatile变量,线程B随后读取了同样的volatile变量,则线程A在写入volatile变量之前的所有可见的变量值,在线程B读取volatile变量后也同样是可见的。
如果线程A读取一个volatile变量,那么线程A中所有可见的变量也会同样从主存重新读取。
完整的volatile可见性保证意味着,在写入varC变量时,线程中所有可见变量也会写入主内存,在读取varC变量时,varA、varB也会从主内存读取。
可以将对volatile变量的读写理解为一个触发刷新的操作,因此,应当尽量将volatile的写入操作放在最后,而将volatile的读取放在最前,这样就能连带将其他变量也进行刷新。
领取专属 10元无门槛券
私享最新 技术干货