并发——Synchronized

一、作用

保证同一时刻只有一个线程可以执行某个方法或代码块(原子性、有序性),同时可保证一个线程对共享变量的操作可以被其他线程及时看到。

二、概念

1. JMM(Java Memory Model)

JMM 用于屏蔽掉各种硬件和操作系统内存的访问差异,以实现 Java 程序在各种平台上都能达到一致的并发效果。JMM 规范了 Java 虚拟内存如何与计算机内存协同工作:规定了一个线程如何和何时可以看到其他线程修改过的共享变量的值,以及在必须时如何同步地访问共享变量。

2. JVM 中对象的内存布局

(1)对象头(Header)

对象头包含2部分信息:1)存储对象自身的运行时数据(Mark Word),如 HashCode、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳灯,这部分数据的长度在32位和64位的虚拟机中分别为32bit64bit;2)类型指针,即指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。该部分数据不是必须的,因为查找对象的元数据信息不一定要经过对象本身。

以下是32位 JVM 下,Mark Word 的默认存储结构(无锁状态)。


无锁状态

32位 JVM 下,其他状态(轻量级锁定、重量级锁定、GC标记、可偏向)下对象的 Mark Word 的存储结构如下。


其他状态

重量级锁也就是 synchronized 对象锁,锁标志位为 10,其指针指向的是monitor对象的起始地址每个对象都存在一个 Monitor 与之关联,对象与 monitor 之间的关联有多种实现方式,monitor 可以与对象一起创建销毁,或当线程试图获取对象锁时创建。

Monitor

Monitor 的数据结构中,主要包含

  • _WaitSet
    保存 ObjectWaiter 列表(每个等待锁的线程都会被封装成 ObjectWaiter 对象)。当在 synchronized 代码块/方法中调用了wait()后,线程等待被唤醒,进入 WaitSet

  • _EntryList
    保存 ObjectWaiter 列表。当多个线程想要访问锁时,首先会进入 _EntryList

  • owner
    指向持有 ObjectMonitor 对象的线程

  • count
    进入计数器

当多个线程想要访问锁时,首先会进入 _EntryList。当线程获取到 Monitor 时,_owner 设置为当前线程,count 加1。若当前线程调用wait(),将释放 monitor,owner 恢复为 null,count 减1,同时线程进入 WaitSet,等待被唤醒。若当前线程执行完毕,则释放 monitor,owner 恢复为 null,count 置为0。

(2)实例数据(Instance Data)
无论是从父类继承下来的,还是子类中定义的,都需要记录下来。

(3)对齐填充(Padding)
该数据不是必然存在的。JVM 要求对象起始地址必须是8字节的整数倍。

3. 线程安全

线程安全:多个线程同时修改共享变量时,会导致某些线程对数据的修改丢失

所以如何避免线程安全问题:

  • 保证共享资源在同一时刻只能由一个线程进行操作(原子性、有序性)
  • 将线程操作的结果及时刷新,保证其他线程可以立即获取到修改后的最新数据(可见性)

4. 等待唤醒机制与 synchronized

在使用notify()/notifyAll()wait()时,必须处于 synchronized 代码块或 synchronized 方法中,否则会抛出IllegalMonitorStateException异常。

因为,调用这几个方法前要求必须拿到当前对象的 monitor,而 synchronized 关键字可以获取 monitor。

三、使用

1. 修饰实例方法

对当前实例进行加锁

2. 修饰静态方法

对当前类对象进行i加锁

3. 修饰代码块

对当前对象进行加锁

四、synchronized 实现同步的原理

1. 修饰代码块

同步代码块实现使用的是monitorentermonitorexit指令实现同步,monitorenter指向代码块开始的位置,monitorexit指向代码块结束的位置。

当一个线程试图获取对象objectrefmonitor的持有权时,

  • 若 monitor 的进入计数器为0
    成功获取,并将 count=1

  • 若当前线程已拥有 monitor 的持有权
    则可重入,count 值加1

  • 若其他线程正在持有objectrefmonitor的持有权
    当前线程被阻塞,知道其他线程调用完成(将 monitor 释放,count 设置为0)

编译器会保证每条monitorenter指令,都有对应的monitorexit指令,不论代码块是异常结束还是正常结束。

2. 修饰方法

修饰方法时无需通过字节码指令来控制同步。JVM 从方法常量池的方法表结构中的ACC_SYNCHRONIZED标志位来判断该方法是否为同步方法。

  • 当调用方法时
    若设置了该标志位,则线程试图去获取 monitor 的持有权

  • 当方法返回时
    释放 monitor

五、评价

参考文献

深入理解Java并发之synchronized实现原理
https://zhuanlan.zhihu.com/p/29881777

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

推荐阅读更多精彩内容