JMM小记

关于JMM的思考

前言

看《Java并发编程的艺术》总在思考一个问题,JMM到底是个什么东西?我们又需要JMM来讨论什么问题?JMM中规定的happens-before规则到底决定了什么,有什么意义?

然而思考了很久,碍于水平有限并不能完全清楚的解答这一系列的问题,但还是决定将最近的一点思考记录下来。万一以后想明白了呢。

那么一个疑问就是关于JMM本身

为什么会有JMM

大致可以这么理解,并发问题的本质是应该串行的方法并行执行了,并操作了不该操作的数据导致了错误的结果。所以设计了悲观锁的机制,让对临界区资源操作的并发程序退化成串行执行。(理解意思即可)

所以引申出并发编程的两个关键问题

第一个问题

当我们评价一个多线程的程序时,第一个想起的总是线程是否安全,那么线程安全到底是指什么?

思考一个最常见的线程安全问题,方法A,B都会访问同样的资源。方法A先执行,方法B再执行,当A未执行完成B就读取了临界区的值,导致了不安全情况的发生。

那么精确的形容这个问题,其实就是不同线程因为都存在对临界区的操作而导致程序必须控制不同线程操作发生的相对顺序,也就是线程的同步问题

第二个问题

正常的多线程程序中,一般通过共享内存来实现线程之间的信息交换,而这实际就是在解决并发编程的通信问题


所以多线程技术讨论的核心都是这两个问题,但这一切可以实现的基础是要求:

  1. 先写的代码运行结果,之后的代码是一定可见的

  2. 代码的运行顺序是和我们所书写顺序一样的

但事与愿违,简单的认为之前写的代码结果一定可以被之后的代码感知是错误的,因为计算机底层的复杂实现,存在缓存。写入的代码不一定刷到了主存中,而读取的那一方也可能直接从缓存中读取而不经过主存。

同样的代码在处理器上的最终执行顺序也并不会和书写顺序一致。

总结上面两点,也就是:

  1. 先后运行的代码,多线程中不一定是内存可见的

  2. 代码执行的顺序一定和书写的顺序不同

而这一切其实都是底层的硬件实现所导致的。所以为了,程序员可以忽略底层细节而快速方便的讨论数据的内存状态(讨论上述两个问题),设计出了JMM这种抽象模型,它规定了Java程序运行时数据可能存在的内存状态,也定义了在内存级别下数据的原子性操作。同时可以看出JMM所讨论的问题正是多线程技术实现的基础

那么在此基础上来分析JMM中讨论的两个核心问题

JMM中为什么要讨论内存可见

JMM抽象示意图如下:

JMM.png

从图可以得知,如果线程A,B之间需要通信,那么必须要经历如下两个步骤:

  1. 线程A把本地内存A中更新过共享变量刷新到主内存中

  2. 线程B到主内存中读取线程A之前更新过的共享变量

这里也就说明了,如果希望程序正确的运行,共享变量内存可见性是十分重要的(如果A修改了某个值,B随后读取,但因为没有将本地内存中的值刷会主内存,而导致应该读到的值没有读到)。通常情况下,从A本地内存写道主内存再读到B的本地内存不是一个原子操作。

JMM中为什么要讨论重排序

开始为了处理多线程带来的问题,一般会想到让多线程退化成单线程,也就是悲观锁。当然对于悲观锁而言重排序没有任何的讨论意义,在保证内存可见的情况下,上一个获得锁对临界区的操作一定是对下一个锁可见的,无论上一个锁内的指令执行顺序如何,因为对当前获得锁的线程而言之前方法的操作都全部完成了顺序根本没有意义,而且JMM模型是允许在悲观锁内进行重排序的。

JMM讨论重排序是处于乐观锁的实现必要,因为悲观锁性能的性能问题,JUC包中的一切都是以CAS及乐观锁的思想进行实现的。这种实现本质是允许多个线程同时操作临界区的,只在关键步骤进行CAS操作进行检查,所以多个线程内各自指令的操作顺序就变得重要且有意义了,Java本地方法的CAS系列操作都通过内存屏障实现了volatile语义的读和写,保证了指令不被重排序乐观锁才有可能实现。

下面的例子也可以说明重排序的问题本质还是破坏了多线程的内存语义,导致了内存不可见。

另外在假设代码中每个读写操作都是原子的(也就是操作立即可见)情况下,重排序仍然会破坏内存的可见性

代码在实际运行时并不是按书写的顺序执行的。为了提高性能,编译器和处理器通常会对指令做重排序(编译器,指令级并行,内存系统三种),在单线程下系统可以自行检查代码之间的依赖关系,没有依赖关系的可以被重排序。在多线程下,线程之间代码的依赖关系显然已经不可能由系统完成,观察如下代码。

class ReorderExample{
    int a = 0;
    boolean flag = false;
    
    public void writer(){
        a = 1;          //1
        flag =true;     //2
    }
    
    public void  reader(){
        if(flag){       //3
            int i = a * a;//4
        }
    }
}

线程B在操作4时是不一定可以看到线程A对a的写入的。因为在线程A中1,2操作并没有依赖关系,被允许重排了,而B进入判断条件后a还没有被赋值,并无法感知到1随后对a的修改。所以即使这里保证了内存被即使刷会主存且强制读取,重排序还是破坏了多线程的语义

一些同样重要的概念

Volatile 在JMM中有多重要

volatile规定了变量如何保证可见性,同时对一个变量的读或写保证为原子性。也就是JMM讨论的核心问题之一,内存的可见性就是以volatile为代表,因为volatile是对于内存可见性的最小实现,所以讨论其他操作的内存语义时都以volatile进行比较。

而volatile的语义又是通过内存屏障来保证的,JMM中讨论的内存 屏障也经过了简化,它对编译器和处理器发出内存屏障的指令,但具体实现取决于不同的硬件设备。

Happens-before

学习JMM难免会困惑happens-before到底是个啥?举例中总会说到volatile的写/读实现了happens-before关系,还是十分令人疑惑。

但可以明确的是Happens-before是一套形容操作间内存可见关系的规则,是JMM为了屏蔽底层的硬件细节(如重排序)而通过volatile, lock等方法和工具为程序员提供的一种便于理解内存可见性的手段。

Happens-before定义了8种规则,这些规则都是具有已有的具体实现上总结出的内存可见规则,但说XXX建立了Happens-before规则就可以简单快速的了解操作间的内存可见情况

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354