博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
3.JUC之volatile
阅读量:5898 次
发布时间:2019-06-19

本文共 2049 字,大约阅读时间需要 6 分钟。

原文链接:

一、简介

volatile是Java语言的关键字,用来修饰可变变量(即该变量不能被final修饰),且必须是至少类内可见。所以它是可以修饰带static的变量。这我自己下定义。

它是被设计用来修饰被不同线程访问和修改的变量。来自 

二、功能

volatile提供了一个高效的同步机制,她在某些情况下可以代替synchronized实现更轻量和高效的同步机制,同时也更为脆弱,更难于掌控。被volatile修饰的变量具有内存可见性,但不具有原子性。至于什么是可见性,前面已经做过简单介绍,接下来我们进一步来看什么是可见性。

1. 内存可见性

首先为什么会出现内存可见性问题呢? 

想完全理解这个问题,请自行阅读《深入理解计算机系统》吧!这里简单说一下,

每个线程都有它自己的线程上下文,包括栈、栈指针、程序计数器、通用目的寄存器和条件码。所有的运行在一个进程里的共享该进程的整个虚拟地址空间。——来自《深入理解计算机系统》

下面这个说法可能并不严谨,甚至是有误,但对我们理解这个问题有帮忙。 

如你所知,所有计算都发生CPU,然而它直接操作主存的效果比较远,不如CPU的缓存区,更远不如寄存器。其次,如上面所有的系统会为每个线程分配自己的线程上下文。在这两个大提前下,可能简化的理解为线程有自己的高速cache,即所有线程操作变量时,都不会直接操作主存。当发生cache miss时,从主存拷贝到cache,这些都是你懂的啦。跟所有的cache一样,都存在一致性的问题。

即是正常情况下什么时候发生cache冲刷回主存并不可控。 

不正常情况下,退出临界区时即刻强制更新主存。另一种情况,即我们要讨论的volatile。被volatile修饰的变量比较特殊,表示直接操作主存,不需要通过cache。直接要用时直接从主存取(注意取出来还是会把值放在自己的上下文,这点后面需要用到),用完写直接回主存。这就是内存可见性

2. 可不完全替代synchronized

之前整理synchronized的时候忘了讲synchronized怎么实现同步的,在这里顺便带出来吧。 

synchronized是通过临界区实现同步的,临界区的同步方式是同一个时间只有最多一个线程进入临界区,也就是说只能保证原临界区具有原子性。这是什么意思呢,先来看一下面例子吧。

void barfoo() {    new Thread(() -> {            for(int v=0; v<100; v++) bar(); }).start(); new Thread(() -> { for(int v=0; v<100; v++) foo(); }).start(); } } int v = 0; void bar() { final int t = v + 1; v++; try { TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(10)); } catch (InterruptedException e) { } if(t != v)System.out.println("not match"); } synchronized void foo() { final int t = v + 1; v++; try { TimeUnit.MILLISECONDS.sleep(RandomUtils.nextInt(10)); } catch (InterruptedException e) { } if(t != v)System.out.println("not match"); }

 

 

执行barfoo()的结果打印了not match。 

synchronized只是通过线程在离开临界区时会把线程上下文冲刷回主存,从而实现一致性,但对于变量v而言不具备原子性,更无法保证能够一致性。

volatile可部分替代synchronized,也就是说在特定条件或者场景下可以替代synchronized。上面我们提到过volatile具有内存可见性,但不具有原子性,而synchronized实际是上能够实现原子性的。这一点是volatile做不到的,也是这种场景下volatile无法代替synchronized。 

这一点就不举例了,主要知道什么是原子性和非原子性即可自行实验了。如:a += b就一个非原子性操作。

三、总结

  1. 简单的了解了volatile的用法;
  2. 进一步了解内存可见性和synchronized实现原理;
  3. volatile与synchronized的差异,以及可代替场景;
  4. volatile通过内存可见性实现同步,即线程A操作了被volatile修饰的变量之后,线程B立马可能读到线程A的修改结果。

我的另外一篇文章也有介绍:

转载于:https://www.cnblogs.com/xuzekun/p/7481872.html

你可能感兴趣的文章
订阅linux kernel的mail list
查看>>
mysql 批量更新多条记录(且不同值)的实现方法
查看>>
Hadoop上路_02-hadoop介绍和环境准备
查看>>
JFinal多参数搜索条件自动组装及参数传递
查看>>
c++ ios_base register_callback方法使用
查看>>
Java中为什么需要Object类,Object类为什么是所有类的父类
查看>>
css3 -webkit-flex 布局
查看>>
大数据Benchmark
查看>>
windows server2008多用户远程登陆设置方法
查看>>
sencha touch巧妙使用请求超时提升用户体验
查看>>
15. 3Sum
查看>>
ArrayList源码解析
查看>>
基于SpringMVC、Maven以及Mybatis的环境搭建
查看>>
angularjs-paste-upload
查看>>
RXjs相关
查看>>
linux基础命令 head
查看>>
objective c:import和include的区别, ""和<>区别
查看>>
spring SchedulerFactoryBean 没有创建 Scheduler的实现类bea
查看>>
基于cobbler实现自动化安装系统
查看>>
The Shared folder with you
查看>>