Synchronized 锁升级

Synchronized 锁升级

锁的基础知识

锁的类型

从宏观上锁分为乐观锁和悲观锁

  • 乐观锁:认为读多写少,遇到并发写情况较少。每次读取数据时都认为数据不会修改,不会加锁;在更新时会判断在这期间是否有数据修改,判断读取的版本号与当前版本号是否一致。java中乐观锁通过cas操作实现。
  • 悲观锁:即认为写多,遇到并发写情况较多。每次读取数据时都会加锁,其他线程读取数据时都要block等待锁释放。java中悲观锁通过Synchronized实现。

JAVA线程阻塞的代价

java 的线程是映射到原生操作系统线程上的,阻塞和唤醒操作系统都需要操作系统介入的,需要在用户态和核心态之间转换。这种切换会耗费大量操作系统资源,因为用户态和核心态都有各自专用的内存空间、寄存器等。用户态切换到内核态需要传递许多变量、参数给内核,内核也需要保存好用户态在切换时的寄存器值和变量。

markword对象头标识

markword是java对象数据结构中的一部分,他的最后2bit是锁状态标识,用来标记当前对象所处的状态。

  • 01:未锁定(对象的hash值、对象年龄分代)
  • 00:轻量级锁(指向锁记录的指针)
  • 10:重量级锁(指向重量级锁的指针)
  • 01:偏向锁(偏向线程ID、偏向时间、对象年龄代)

Synchronized 实现原理

实现原理图如下:


image.png

当多个线程同时访问某个监控对象时,对象监控器会将这些线程存储在不同容器中。

  1. ContentionList:竞争队列,所有线程首先存储在竞争队列中
  2. EntryList:竞争队列中有资格成为候选资源的线程被移动到EntryList中
  3. WaitSet:调用wait()方法的阻塞线程放置在WaitSet中
  4. onDeck:任意时刻最多只有一个线程在竞争锁资源,该线程为OnDeck
  5. Owner:当前已经取得资源的线程为Owner

JVM每次从对尾取出一个线程作为竞争候选者(OnDeck),在并发情况下ContentionList中的并发线程执行CAS访问。JVM 会对部分线程移动到EntryList作为候选竞争线程。Owner线程会在unlock时,将部分ContentionList中的线程移动到EntryList中,并指定某个EntryList中的线程为OnDeck线程,OnDeck需要重新竞争锁,竞争切换。

OnDeck在取得资源时会变为Owner线程,如果没有取得资源仍会留在EntryList中,如果Owner线程调用wait()方法阻塞,则会转移到WaitSet中,直到调用notify或者notifyAll唤醒,重新进入EntryList中。

锁升级

锁的状态:无锁状态、偏向锁、轻量级锁状态、重量级锁状态(级别由低到高)

偏向锁

偏向锁他会偏向第一次访问的线程,当线程获取锁对象时,会在java对象头markword中记录偏向锁的threadID,并不会主动释放偏向锁。当同一个线程再次获取锁时会比较当前的threadID与对象头中的threadID是否一致。如果一致则不需要通过CAS来加锁、解锁。如果不一致并且线程还需要持续持有锁,则暂停当前线程撤销偏向锁,升级为轻量级锁。如果不在需要持续持有锁则锁对象头设为无锁状态,重新设置偏向锁。

偏向锁过程:

  1. 访问Mark Word中偏向锁的标识是否设置成1,锁标识位是否为01,确认偏向状态
  2. 如果为可偏向状态,则判断当前线程ID是否为偏向线程
  3. 如果偏向线程未只想当前线程,则通过cas操作竞争锁,如果竞争成功则操作Mark Word中线程ID设置为当前线程ID
  4. 如果cas偏向锁获取失败,则挂起当前偏向锁线程,偏向锁升级为轻量级锁。

轻量级锁

轻量级锁由偏向锁升级而来,偏向锁运行在一个线程同步块时,第二个线程加入锁竞争的时候,偏向锁就会升级为轻量级锁。

轻量级锁过程:

  1. 线程由偏向锁升级为轻量级锁时,会先把锁的对象头MarkWord复制一份到线程的栈帧中,建立一个名为锁记录空间(Lock Record),用于存储当前Mark Word的拷贝。
  2. 虚拟机使用cas操作尝试将对象的Mark Word指向Lock Record的指针,并将Lock record里的owner指针指对象的Mark Word。
  3. 如果cas操作成功,则该线程拥有了对象的轻量级锁。第二个线程cas自选锁等待锁线程释放锁。
  4. 如果多个线程竞争锁,轻量级锁要膨胀为重量级锁,Mark Word中存储的就是指向重量级锁(互斥量)的指针。其他等待线程进入阻塞状态。

synchronized的执行过程:

  1. 检测Mark Word里面是不是当前线程的ID,如果是,表示当前线程处于偏向锁
  2. 如果不是,则使用CAS将当前线程的ID替换Mard Word,如果成功则表示当前线程获得偏向锁,置偏向标志位1
  3. 如果失败,则说明发生竞争,撤销偏向锁,进而升级为轻量级锁。
  4. 当前线程使用CAS将对象头的Mark Word替换为锁记录指针,如果成功,当前线程获得锁
  5. 如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
  6. 如果自旋成功则依然处于轻量级状态。
  7. 如果自旋失败,则升级为重量级锁。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容