zoukankan      html  css  js  c++  java
  • synchronized锁原理monitor

    monitor(监视器/管程)


    java对象分三部分,

    1. 对象头
    2. 数据实例
    3. 填充

    对象头分为

    1. 普通对象- markword(32bit)/klass word(32bit)(指向对应的class对象)
    2. 数组对象-多一个array length(32bit)数组长度

    markword的结构

    1. hashcode(25) age(4) biased_lock:0(代表是否是偏向锁) | 01 (代表加锁状态) normal状态(正常状态)
    2. thread:54(线程id) epoch:2 age(4) biased_lock:1(代表是偏向锁)|01(代表加锁状态) biased(偏向锁)
    3. ptr_to_heavyweight_monitor(30,指向monitor的指针) |10(代表加了重量级 heavyweight lock(重量级锁定)
    4. ptr_to_lock_record:30 (30位代表锁记录的地址) |00(代表加轻锁) LightWeight Lock(轻量级锁定)

    monitor是操作系统提供的,内部结构

    1. waitset

    2. entrylist(等待队列)

    3. owner(monitor拥有者)

      `具体步骤1是字节码中的monitorenter操作指令 步骤4是字节码中monitorexit指令
      重量级锁的加锁流程:
      1. thread0执行synchronize代码的时候,synchronized(obj)的obj对象的markword中ptr_to_heavyweight_monitor会指向一个monitor对象,monitor的owner是thread0
      2. thread1执行到synchronized代码时,发现obj的markword指向了一个monitor并且owner有人了,这时会进入entrylist进行blocked
      3. thread2也一样
      4. thread0执行完同步代码退出synchronized,把obj markword里的数据还原比如hashcode age啥的,这些数据是存在monitor对象中的,然后唤醒entrylist的thread1和thread2的blocked线程,两个线程去抢owner
      5. 如果出现异常,jvm会自动释放锁 执行第4步

    `
    因为获取monitor是系统指令,需要从用户态转为内核态,出现上下文切换啥的,比较耗费资源,所以java1.6版本对synchronized进行了优化

    1. 轻量级锁/使用场景是一个对象虽然有多个线程访问,但是不出现竞争,这时使用轻量级锁来优化
      加锁流程:

      1. 创建锁记录(lock record)对象(内部存储锁定对象的mark word,还有一个对象指针记录)
      2. 让锁记录中的object reference指向锁对象,将锁对象的mark word值存入锁记录(这时锁记录记录的是hashcode age 01无锁状态,obj锁对象对象头中存储的是锁记录地址和状态00 锁添加成功
      3. 如果obj锁对象中的状态已经是00锁记录存储的是其他线程这时加锁失败
        cas替换失败了有两种情况
        1.如果其他线程已经持有了该object的轻量级锁,这时出现了竞争,进行锁膨胀
        2.如果是线程自己执行了锁重入,添加一条lock record在锁持有线程的栈帧中 指向obj锁对象 并进行cas操作(一定失败),作为重入的计数

      释放锁流程:

      1. 退出同步代码块时,如果有锁记录为null的情况表示有重入,重置锁记录表示重入计数减一
      2. 当锁记录不为null,这时使用cas操作恢复markword给obj锁对象的对象头
        两种情况:
      3. 恢复成功 解锁成功
      4. 失败说明轻量级锁进行了锁膨胀,这时需要执行重量级锁的解锁流程

    锁膨胀:
    轻量级锁出现了竞争(在尝试加轻量级锁的过程中,其他线程为此对象已经加上了轻量级锁),这时需要进行锁膨胀,升级为重量级锁
    thread0持有着轻量级锁,thread1进来加轻量级锁,出现竞争这时thread1加锁失败进行锁膨胀,
    1. thread1为obj对象申请monitor对象,并将obj锁对象的mark word中指向monitor,thread1自己进入entrylist进行blocked
    2. 当thread0执行完同步代码块,解锁的时候发现锁的标识变了,mark word也不是指向栈帧中的锁记录了,这时执行重量级锁的解锁流程

    自旋优化(适用于多核处理器)
    重量级锁出现竞争的时候,使用自旋来优化

    偏向锁(优先加偏向锁)
    轻量级锁在加锁成功后,锁重入的时候会再执行一次cas的操作(虽然必定失败),这样会浪费资源
    偏向锁的加锁流程:
    第一次使用cas操作将线程的id存入obj对象头的markword,之后发现这个id是自己就说明没有竞争,不用重新cas操作,只要不发生竞争,这个锁就归这个线程所有
    如果调用对象的hashcode,会撤销obj锁对象的偏向状态
    当申请obj对象锁的线程是不偏向的那个线程的时候(这时没有竞争),会升级为轻量级锁
    批量重偏向
    当偏向锁撤销超过20次,重偏向给加锁的线程
    批量撤销
    当偏向锁撤销超过40次,整个类的所有对象都变为不可偏向的
    锁消除
    jit及时编译器在运行时会优化掉

  • 相关阅读:
    vue /deep/ ::v-deep >>> 深度选择器
    雪碧图优缺点
    自适应高度文本框 react contenteditable
    textarea 高度自适应
    UMI.js开发知识总结
    flex布局下img图片变形的解决方法
    umi model 注册
    HEVC标准介绍
    《推荐系统》阅读笔记
    互联网名词诙谐解释
  • 原文地址:https://www.cnblogs.com/isnotnull/p/13934420.html
Copyright © 2011-2022 走看看