JAVA多线程-(七)synchronized关键字详解

用户态与内核态

JDK早期,synchronized 叫做重量级锁, 因为申请锁资源必须通过kernel, 系统调用。
操作系统内核可以访问所有指令,用户态必须通过操作系统调用内核可以访问的命令是内核态可以直接访问的。

对象内存布局之markword

普通对象
1:第一是对象头,在hotport里面称为markword 长度是8个字节
2:第二个是ClassPointer指针:-XX:+UseCompressedClassPointers 为4字节 不开启为8字节。通过这个能找到xx.class这个类。
3:第三个是实例数据
引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节
Oops Ordinary Object Pointers
4:Padding对齐,这个对齐是8的倍数
工具:JOL = Java Object Layout可以打印输出对象在内存的布局信息markword
JOL使用:

   <!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>

import org.openjdk.jol.info.ClassLayout;

public class T04_HelloJol {
    public static void main(String[] args){

        Object o=new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());//o

         synchronized(o){
              System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }
}

不加synchronized和加上synchronized关键字对象头markword的区别

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001(001无锁态) 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           08 f7 f7 02 (00001000(00轻量级锁) 11110111 11110111 00000010) (49805064)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
锁信息在markword里边  GC和hashcode也在markword里边

001表示无锁态;00是轻量级锁;
在这里插入图片描述
锁升级
普通对象加关键字-偏向锁-轻量级锁(自旋锁/无锁)-重量级锁(向操作系统申请的)
在这里插入图片描述
偏向锁和轻量级锁是用户空间锁,用户态的锁,不需要和操作系统申请,轻量级:偏向锁->轻量级锁
用户空间锁VS重量级锁

  • 偏向锁 自旋锁 都是用户空间完成
  • 重量级锁是需要向内核申请

偏向锁
第一个访问这把锁的线程,把当前线程的指针放在markword上面,没有锁竞争。

自旋锁 指向线程栈中lock record的指针有别的线程竞争偏向锁,先把偏向锁撤销,即当前使用锁线程的指针从markword上面删除。
自旋锁竞争
多个线程竞争锁先撤销偏向锁,如果没有偏向锁直接竞争自旋锁,通过自旋锁竞争,每个线程都有线程栈专属线程私有,每个线程栈内部生成一个LR即Lock Record锁记录,用自旋方式,哪个线程将LR放在锁上哪个线程持有锁,没有锁的线程在自旋尝试哦等待获取锁

重量级锁
必须向操作系统申请

偏向锁:线程来了先不加锁,记录线程ID值,表示锁是线程独有,偏向于第一次拿到这把锁的线程,下次访问这把锁还是这个线程直接访问锁资源。如果来的新线程ID和锁上表示的线程ID不等,就会锁升级,先进行自旋锁的升级,自旋锁就是锁已经被别的线程拿到,再来的线程想拿到锁在这转圈10次,如果拿不到升级成重量级锁,重量级锁申请的时候要经过OS,进入等待队列,进入等待队列不再占用CPU时间。

java关键字synchronized编译后字节码
monitorenter/monitorexit JVM汇编
源文件

synchronized(o){//monitorenter 锁开始
//monitorexit异常退出
               }//monitorexit 正常释放锁

字节码

24 monitorenter 
39 monitorexit
45 monitorexit

自动上锁 自动释放锁

InterpreterRuntime:: monitorenter方法

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))

#ifdef ASSERT

  thread->last_frame().interpreter_frame_verify_monitor(elem);

#endif

  if (PrintBiasedLockingStatistics) {

    Atomic::inc(BiasedLocking::slow_path_entry_count_addr());

  }

  Handle h_obj(thread, elem->obj());

  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),

         "must be NULL or an object");

  if (UseBiasedLocking) {//如果是同偏向锁

    // Retry fast entry if bias is revoked to avoid unnecessary inflation

    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);

  } else {

    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);

  }

  assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),

         "must be NULL or an object");

#ifdef ASSERT

  thread->last_frame().interpreter_frame_verify_monitor(elem);

#endif

IRT_END

synchronizer.cpp
revoke_and_rebias

void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) {

 if (UseBiasedLocking) {

    if (!SafepointSynchronize::is_at_safepoint()) {

      BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);

      if (cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {

        return;

      }

    } else {

      assert(!attempt_rebias, "can not rebias toward VM thread");

      BiasedLocking::revoke_at_safepoint(obj);

    }

    assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now");

 }



 slow_enter (obj, lock, THREAD) ;

}
void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {

  markOop mark = obj->mark();

  assert(!mark->has_bias_pattern(), "should not see bias pattern here");



  if (mark->is_neutral()) {

    // Anticipate successful CAS -- the ST of the displaced mark must

    // be visible <= the ST performed by the CAS.

    lock->set_displaced_header(mark);

    if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(), mark)) {

      TEVENT (slow_enter: release stacklock) ;

      return ;

    }

    // Fall through to inflate() ...

  } else

  if (mark->has_locker() && THREAD->is_lock_owned((address)mark->locker())) {

    assert(lock != mark->locker(), "must not re-lock the same lock");

    assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock");

    lock->set_displaced_header(NULL);

    return;

  }



#if 0

  // The following optimization isn't particularly useful.

  if (mark->has_monitor() && mark->monitor()->is_entered(THREAD)) {

    lock->set_displaced_header (NULL) ;

    return ;

  }

#endif
  // The object header will never be displaced to this lock,

  // so it does not matter what the value is, except that it

  // must be non-zero to avoid looking like a re-entrant lock,

  // and must not look locked either.

  lock->set_displaced_header(markOopDesc::unused_mark());

  ObjectSynchronizer::inflate(THREAD, obj())->enter(THREAD);

}

inflate方法:膨胀为重量级锁

锁重入

synchronized可重入锁
重入次数必须记录,进去几次解几次
偏向锁记录在线程栈,每重入一次LR加1
在这里插入图片描述
无锁状态有hashcode,偏向锁hashcode存在线程栈,LR有一个指针指向前一个用来做备份的markword

synchronized实现细节

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值