深入理解volatile

Java内存模型

在计算机中,所有的运算操作都是由CpU的寄存器来完成的,在CPU Cache模型没出来之前,CPU所访问的数据只能是计算机的主存,但CPU本身的计算速度与主内存的读写速度远远不一致,所以在中间添加了Cache模型,在程序运行的时候,程序会把从内存中读取的数据复制一份到Cache中,然后直接对CPU cache中的数据进行读取和写入,当运算结束后,再将CPU cache中的最新数据刷新到主内存中。

cpu通过Cache与主内存进行交互.png

但在多线程情况下,利用该机制,可能会出现缓存不一致的现象。
典型的解决办法有:

  • 通过总线加锁,只允许一个CPU抢到总线锁,来访问这个变量的内存。

  • 通过缓存一致性协议
    例如:在读取操作时,不做任何处理,只是将Cache中的数据读取到寄存器中
    在写入操作时,通知其他CPU将该变量的Cache line置为无效状态,其他CPU在进行该变量读取时不得不到主内存中再次获取。

  • 共享变量存储在主内存当中,每个线程都可以访问。

  • 每个线程都有私有得到工作内存。

  • 工作内存只存储该线程对共享变量的副本。

  • 线程不能直接操作主内存,只有先操作了工作内存才能写入到主内存。

java内存模型.png

并发编程的三个重要特性

  • 原子性
    指在一次的操作或多次的操作中,要么所有的操作都执行并不会受到任何元素的干扰而中断,要么所有的操作都不执行
    note: 两个原子性的操作结合在一起未必还是原子性的, 例如i++
  • 可见性
    当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的最新值
  • 有序性
    是指程序代码在执行过程中的先后顺序,
    有时,处理器为了提高程序的运行效率,可能会对输入的指令做一定的优化,它不会完全按照代码的执行顺序进行,但是它会保证程序的最终运算结果是编码时所期望的那样,即指令的重排序
    在单线程情况下,无论怎样的重排序最终都会保证程序的执行效果和代码顺序执行的结果是完全一致的,但是在多线程情况下,如果有序性得不到保证,那么很有可能会出现很大的问题

JMM与原子性

在Java中,对基本数据类型的变量读取赋值操作都是原子性的,对引用类型的变量读取和赋值的操作也是原子性的。

  • x = 10 是原子性的
    首先将x=10写入到工作内存中,然后再将其写入到主内存中
  • y = x 是非原子性的
    1. 从主内存中读取x的值(如果x已经在执行线程的工作内存中,则直接获取)然后将其存入当前线程的工作内存中
    2. 在执行线程的工作内存中修改y的值为x,然后将y的值写入到主内存中
  • y++ 是非原子性的
    1. 执行线程从主内存中读取y的值(如果y已经存在了执行线程的工作内存中,则直接获取),然后将其内存当前线程的工作内存之中。
    2. 在执行线程工作内存中为y执行加1操作
    3. 将y的值写入到主内存中
  • z = z + 1 是非原子性的
    同上

jMM与可见性

java中提供了以下三种方式来保证可见性

  • 使用关键字volatile,对数据的读操作直接在主内存中读取(当然也会缓存到工作内存中,当其他线程对该共享线程进行了修改,则会导致当前线程在工作内存中的共享资源失效,所以必须从主内存中再次获取),对于共享资源的写操作当然是先要修改工作内存,但是修改结束后会立即将其刷新到主内存中
  • 通过synchronized关键字能够保证可见性,同一时刻只有一个线程获得锁
  • 通过JUC提供的显式锁Lock也能保证可见性

volatile关键字解析

该类变量具备下面两层语义:

  1. 保证了不同线程之间对共享变量操作时的可见性,也就是说当一个线程修改volatile修饰的变量,另一个线程会立即看到最新的值。
  2. 禁止对指令进行重排序操作。
    但volatile不具备原子性
    有一个经典的例子 i++操作
    如果有两个线程执行该操作时,当一个线程读取到i的当前值后,停止,然后跳转到另一个线程读取i的值,然后执行+1操作,并将值返回到主内存中,有的读者可能会产生这样的疑惑:
    volatile具有可见性,当一个线程修改后,另一个线程会立即看到最新的值,所以当第一个线程暂停回来后会从主内存中读取到最新的值,并执行+1操作,但实际情况是两个线程的结果是一样的,比如i=10,两个线程执行 volatile i++操作,都得到了11,理想的结果是一个11,一个12,为什么呢?
    volatile的可见性保证你每次访问到该变量时,都会读取到最新的值,但是并不会更新你已经读的值,它也无法更新你已经读了的值。上文中第一个线程是读取后再停止的,此时i值还没有被修改,当另一个线程修改完成后,该线程继续执行接下来的i+1操作,此时的i已经是已被读的值了,不会到主内存中获取最新的值,保留的是最初的值,所以产生了错误。

volatile的使用场景

  1. 开关控制利用可见性的特点
 public class MyThread extends Thread {
 private volatile boolean started = true;
 @Override
 public void run() {
     while(started) {
         
     }
 }
 public void shutdown(){
     this.started = false;
 }
}
  1. 状态标记利用了顺序性特点
private volatile boolean init = false;
private Context context;
public Context load() {
    if (!init) {
        context = loadContext();
        init = true; //防止重排序
    }
    return context;
}
  1. Singleton设计模式的double-check也是利用了顺序性特点

volatile和synchronized

  1. 使用上的区别
  • volatile关键字只能用于修饰实例变量或者类变量,不能用于修饰方法以及方法参数和局部变量、常量。
  • synchronized关键字不能用于对变量的修饰,只能用于修饰方法或者语句块
  • volatile修饰的变量可以为null,synchronized关键字同步语句块的monitor对象不能为null
  1. 对原子性的保证
  • volatile无法保证原子性,但后者可以保证
  1. 对可见性的保证
    都能保证多线程间的可见性,但是实现机制不同。
  • synchronized借助JVM指令monitor enter和monitor exit
  • volatile使用机器指令lock
  1. 对有序性的保证
  • volatile禁止重排序,但后者可能会发生指令重排序的情况
  1. 线程是否阻塞
  • volatile不会使线程陷入阻塞,而后者会发生这种情况
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容