两个关键词 volatile synchronized
-
volatile:保证在多核处理器下,所有线程看到的变量的值是一致的。(volatile还有一个语义为禁止指令重排序,此处暂且不表)
- 要解释volatile,就不得不提CPU的三层缓存结构:
cache line:现代操作系统,核心在计算时,一般是按照缓存行(cache line)来进行处理的。每次从内存加载数据时,为了访问的速度,会将目标数据及其相邻的数据,一起载入缓存,这部分就叫做缓存行,有个大小限制,一般为64bit。缓存与内存的交互都是以缓存行为单位进行的。
volatile提出背景:作为Java中的普通对象,当Thread1跑在Core1,对独享变量进行了变更时,Core2上的Thread2是不会立刻感知到的。(等到Thread1将更新写回内存,Thread2再从内存中重载这块内存时才能刷新,在此之前,Thread2拿到的都是旧数据)。 所以提出了volatile,当变量需要在线程间共享时,需要在变量前加上volatile关键字。
原理:volatile变量经过汇编后,会在基础的指令上,增加一个Lock前缀指令。- Lock有什么用:
- 将当前缓存行的数据刷回内存
- 其他Core会监听总线上Lock的动作(MESI)
- 具体流程:JMM的8中原子操作(read load use assign write store lock unlock)
- T1: read-load-use
- T2: read-load-use
- T1: (lock)assign-store-wrtie (unlock)T2监听到,将cacheline失效
- T2有读请求时:read-load-use
- 支线:MESI:缓存一致性协议,为CPU层面,对缓存行进行的四中标记,以确保各个Core上缓存的一致性:
- modifide
- exclusive
- shared
- invalid
各状态间的流转见下图:
- 要解释volatile,就不得不提CPU的三层缓存结构:
-
synchronized:对线程对临界区代码的访问进行加锁控制
- 锁的什么东西:
- 普通同步方法:锁定实例对象
- 静态同步方法:锁定当前类对象
- 同步方法块:锁定括号中配置的对象。
- 实现:通过在临界区代码块的前后插入指令实现。
- 先来谈谈Java对象头(以64bit为例)
-
Mark word(64bit) 锁信息或hashCode
无锁情况下的对象头:
施加偏向锁时的对象头:
-
Class MetaData(64bit) 类指针
-
array length(32bit):数组大小(也说明数组最长只能到 2^31-1,这也是通过构造函数能创建的,最长的字符串长度了)。
-
- 在Java 1.6之前,所有的锁都是重量级的,但是经过数据分析后发现,并发场景下的大部分锁往往只由一个线程进行了访问,也以此做出假设,在Java1.6中引入了锁升级机制。
- 锁状态:无锁、偏向锁、轻量级锁、重量级锁
- 相关前情提要:
- CAS: compare and swap 原子操作
- 锁升级流程:(私信获取原图)
偏向锁:同时只有一个线程时,用户态
轻量级锁:2个线程产生竞争时,用户态
重量级锁:当第二个线程在CAS自旋等待时。为了避免多数的线程CAS自旋造成的CPU浪费,在内核态,通过重量级锁,将CAS自旋的线程进行挂起。
- 锁的什么东西: