并发编程底层原理

Markdown语法

JMM(Java Memory Model)

是一种规范
是工具和关键字的原理
volatile, synchronized, Lock 等的原理都是JMM
最最重要的3点内容:重排序,可见性,原子性

重排序

  • 什么是重排序

线程内部两行代码的执行顺序和java 文件中的执行顺序不一致,指令并没有按照最初编写的代码顺序执行,而是改变了,这就是重排序。

  • 重排序的好处:提高处理速度
  • 重排序的3种情况:
    1. 编译器做优化
    2. CPU指令重排,就算编译器不重排,CPU也可能会对指令重排
    3. 内存的“重排序”:这个“重排序”不是真正的重排序,是现象上的重排序,看上去和重排序一样的效果,因为缓存的存在(主存和本地内存),不一定实时保持一致,所以可能会表现出和重排序一样的现象

可见性

为什么有可见性问题
CPU缓存结构.png

CPU有多级缓存导致读的数据过期

  • 高速缓存的容量比主内存小,但是速度仅次于寄存器,所以在CPU和主内存之间多了cache层
  • 线程间对于共享变量的可见性问题不是直接由多核引起的,而是由多缓存引起的。
  • 如果所有核心都只用一个缓存就不存在内存可见性问题了
  • 每个核心都会将自己需要的数据读到独占缓存中,数据修改后也是写入到缓存中,然后等待刷入到主存。所以会导致有的核心读到的数据是一个过期值。
什么是主内存和本地内存

Java屏蔽底层细节,用JMM定义了一套读写内存的规范,抽象了主内存、本地内存的概念,使我们不再需要关心一级缓存,二级缓存的问题。
这里说的本地内存并不是给每个线程真的分配一块内存,而是JMM对寄存器,一级缓存,二级缓存等做的抽象。

主内存和本地内存的关系

JMM的规定:

  1. 所有的变量都存贮在主内存中,同时每个线程都有自己独立的工作内存,工作内存的变量是主内存的拷贝。
  2. 线程不能直接读写主内存中的变量,只能操作自己工作内存中的变量,然后再同步到主内存中。
  3. 主内存是多个线程共享的,但线程之间不共享工作内存,如果线程间需要通信,必需通过主内存中转。
Happens-Before原则

什么是Happens-Before
是用来解决可见性问题的:在时间上,动作A发生在动作B之前,B保证能看见A,这就是happens-before。
如果一个操作happens-before于另一个操作,那么第一个操作对于第二个操作是可见的。

happens-before规则有哪些?

  1. 单线程规则
  2. 锁操作(Synchronized和Lock)
  3. volatile修饰变量
  4. 线程join
  5. 传递性: 如果hb(AB),且hb(BC),则hb(AC)成立
  6. 工具类的happens-before原则
    1)线程安全的容器,get一定能看到之前的put动作
    2)CountDownLatch
    3)Semaphore 信号量
    4)CyclicBarrier
volatile关键字

volatile 是什么
volatile是一种同步机制,比Synchronized和Lock相关类更轻量,使用volatile并不会发生上下文切换等开销很大的行为。
开销小,能力也小,volatile做不到Synchronized那样的原子保护,只在很有限的场景下发挥作用。

volatile的适用场合
不适用:a++
适用场合1:如果一个变量自始至终只有被各个线程直接赋值,没有别的操作,可以用volatile代替Synchronized和原子变量,因为赋值自身是原子的,volatile保证可见,所以就线程安全了。

适用场合2:作为变量的触发器

volatile的作用:可见性,禁止重排序
  • 可见性:读一个volatile变量之前,需要先使响应的本地内存的变量失效,这样就必需到主内存读取最新值。写一个volatile属性会立刻刷到主内存。
  • 禁止指令重排序:例如可以解决单例双重锁乱序的问题。

volatile和Synchronized的关系
如果一个变量自始至终只有被各个线程直接赋值,没有别的操作,可以用volatile代替Synchronized。

volatile提供了happens-before保证。

volatile可以使得long和double的赋值是原子的。

Synchronized不仅保证了原子性,还保证了可见性。不需要多解释了

原子性

什么是原子性

一系类的操作,要么全部执行成功,要么全部不执行,不会出现执行一半的情况,原子是最小单位,是不可分割的。

Java中的原子操作有哪些
  • 除long和double之外的基本类型(byte, short, int, float, boolean, char)的赋值操作。
  • 所有引用reference的赋值操作,不管32位还是64位机器。
  • java.util.concurrent.atomic下面的所有原子类的操作
long 和double的原子性

问题描述:long, double 由于是64位的值,每次写入会被看作是两次单独的写入,每次写入32位,写一半,导致写入不是原子的操作。这种行为是特定于虚拟机实现的,Oracle文档中:“鼓励”虚拟机避免拆分64位,如果可以写一次就行不要拆分,但是鼓励程序员按照拆分64位的情况处理,建议将其声明为volatile的变量。
思考:在32位的jvm上,long,double的操作不是原子的,但是在64位的jvm上是原子的。
现实:但从实际上说,现有的商用JVM,都在虚拟机实现的层面上保证了long, double的写入是原子的了,也都听从了Oracle文档的“鼓励”

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

推荐阅读更多精彩内容