Synchronized(二)

上集说到,Java的对象头里可以标记对象锁的状态:无锁,偏向锁,轻量级锁,重量级锁,我们进一步分析四种锁的状态

Java 中的锁

在 Java 中主要2种加锁机制:

  • synchronized 关键字
    上集分析过是通过一对字节码指令 monitorenter/monitorexit 实现, 这对指令被 JVM 规范所描述。
  • java.util.concurrent.Lock
    Lock是一个接口,ReentrantLock是该接口一个很常用的实现
    通过 Java 代码搭配sun.misc.Unsafe 中的本地调用实现的

CAS 指令(Compare And Swap)

Synchronized在底层用汇编指令cmpxchg来实现CAS指令:语义可以用伪代码表示。CAS指令是原子性的

// 入参三个参数p(目的地址),old(值1),new(值2)
// 指令会比较目的地址和值1是否一致
// 如果不一致,返回false
// 如果一致,将值2填写到目的地址,返回true
function cas(p, old, new) returns bool {
    if(*p != old) {
        return false
    }
    *p = new
    return true
}

锁升级--- 偏向锁,轻量级锁,重量级锁


java对象头标志位表示了3种锁的状态,根据竞争的程度,偏向锁会变成轻量级锁,轻量级锁再更激烈的竞争下回变成重量级锁。

回忆上集提到过的Java对象头,下图体现了三种锁状态切换

无锁->偏向锁(Normal ->Biased)

偏向锁的获取方式是将MarkWord部分,标记上线程ID,赋值逻辑:

  • 获取MarkWord,判断是否可以处于可偏向的状态

找到了openjdk的源码MarkWord.hpp

  // Indicates that the mark has the bias bit set but that it has not
  // yet been biased toward a particular thread
  bool is_biased_anonymously() const {
    return (has_bias_pattern() && (biased_locker() == NULL));
  }

  // Biased Locking accessors.
  // These must be checked by all code which calls into the
  // ObjectSynchronizer and other code. The biasing is not understood
  // by the lower-level CAS-based locking code, although the runtime
  // fixes up biased locks to be compatible with it when a bias is
  // revoked.
  bool has_bias_pattern() const {
    return (mask_bits(value(), biased_lock_mask_in_place) == biased_lock_pattern);
  }
  JavaThread* biased_locker() const {
    assert(has_bias_pattern(), "should not call this otherwise");
    return (JavaThread*) mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place));
  }

has_bias_pattern() 返回true代表可偏向标志(biased_lock)为1,lock标志位01

biased_locker() == Null 返回true 表示MardWord里thread是空

如果为可偏向状态,尝试CAS操作。

  • CAS操作成功,变为偏向的状态
  • CAS失败,有另外一个线程 Thread B 抢先获取了偏向锁。 这种状态说明该对象的竞争比较激烈, 此时需要撤销 Thread B 获得的偏向锁,将 Thread B 持有的锁升级为轻量级锁。 该操作需要等待全局安全点 JVM safepoint ( 此时间点, 没有线程在执行字节码) 。

如果是已偏向状态, 则检测 MarkWord 中存储的 thread ID 是否等于当前 thread ID

  • 如果相等, 则证明本线程已经获取到偏向锁, 可以直接继续执行同步代码块
  • 如果不等, 则证明该对象目前偏向于其他线程, 需要撤销偏向锁

偏向锁撤销流程

1.在一个安全点停止拥有锁的线程。
2.遍历线程栈,如果存在锁记录的话,需要修复锁记录和Markword,使其变成无锁状态。
3.唤醒当前线程,将当前锁升级成轻量级锁。
所以,如果某些同步代码块大多数情况下都是有两个及以上的线程竞争的话,系统开销大,我的可以关闭偏向锁,JVM有参数可以设置

-XX:-UseBiasedLocking

偏向锁->轻量级锁(Biased->Lightweight)

前面提到,多个线程竞争一个对象时,发生偏向锁的撤销操作。对象可能处于两种状态:

不可偏向的无锁状态,原来已经执行了同步快代码,对象处于闲置状态:


可以偏向的已锁状态(轻量级锁),原来已经获取了偏向锁的线程尚未执行同步代码块,偏向锁依旧有效:

轻量级加锁过程:

  • 通过标志位判断出对象状态处于不可偏向的无锁状态

  • 当前线程的栈帧创建存储锁记录的空间,将对象头复制到锁记录中

  • CAS操作来将对象头的MarkWord替换为指向锁记录的记录的指针

    • 成功,当前线程获得锁
    • 失败,已经加锁,自旋,再次尝试CAS操作,仍未抢到,升级为重量级锁,就是锁膨胀

轻量级锁也被称为非阻塞同步、乐观锁,因为这个过程并没有把线程阻塞挂起,而是让线程空循环等待,串行执行。


锁膨胀示意

引用大佬的图展示锁竞争,最终膨胀为重量级锁的过程。



重量级锁

Synchronized没有优化之前,是重量级锁,依赖对象内部的monitor锁来实现,而monitor锁依赖操作系统的MutexLock(互斥锁)来实现

为什么重量级锁开销大?

主要是,当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cpu。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。

互斥锁(重量级锁)也称为阻塞同步,悲观锁

下面总结一下三个形态锁的优缺点:

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外消耗 线程间存在锁竞争,会带来额外锁撤销的消耗 只有一个线程访问同步快
轻量级锁 竞争的线程不会阻塞,提高程序相信速度 如果始终得不到锁竞争的线程,消耗CPU 追求响应时间,同步块实行速度特别快
重量级锁 线程竞争不使用自旋,不会消耗CPU 线程阻塞,响应时间慢 追求吞吐量,同步块时间时间长

总结

通过上面的分析,synchronized关键字并非一开始就该对象加上重量级锁,也是从偏向锁,轻量级锁,再到重量级锁的过程。

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

推荐阅读更多精彩内容