Java并发编程(五)

Java并发编程(五)

Monitor

Java对象头

JVM中对象头的方式有以下两种(以32位为例)
  • 1.1 Java普通对象
|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |    Klass Word (32 bits) |
|------------------------------------|-------------------------|
  • 1.2 数组对象
|---------------------------------------------------------------------------------|
|                                 Object Header (96 bits)                         |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|
  • array length
    如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,这部分数据的长度也随着JVM架构的不同而不同:32位的JVM上,长度为32位;64位JVM则为64位。
对象头的组成

对象头主要用来存储对象自身的运行时数据,如hashcode、gc分代年龄等。mark word的位长度为JVM的一个Word大小,也就是说32位JVM的Mark word为32位,64位JVM为64位。为了让一个字大小存储更多的信息,JVM将字的最低两个位设置为标记位,不同标记位下的Mark Word示意如下:

Mark Word (32 bits)--是否偏向标志位锁状态
identity_hashcode:25age:4biased_lock:1lock:2Normal 无锁
thread:23epoch:2age:4biased_lock:1lock:2Biased 偏向锁
ptr_to_lock_record:30lock:2Lightweight Locked 轻量级锁
ptr_to_heavyweight_monitor:30lock:2Heavyweight Locked 重量级锁
lock:2Marked for GC (GC标记)
  • biased_lock:对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。
  • age:4位的Java对象年龄。在GC中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15
  • thread:持有偏向锁的线程ID。
  • epoch:偏向时间戳。
  • ptr_to_lock_record:指向栈中锁记录的指针。
  • ptr_to_heavyweight_monitor:指向管程Monitor的指针。

64位的Mark Work 如下

在这里插入图片描述

  • 锁标记位的状态如下:(该标记的值不同,整个mark word表示的含义不同。)
biased_locklock状态
001无锁
101偏向锁
000轻量级锁
010重量级锁
011GC标记

Monitor

  • Monitor 被翻译为监视器或管程。
  • 每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针
Monitor结构如下

在这里插入图片描述

  • 刚开始 Monitor 中 Owner 为 null
  • 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner
  • 在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList BLOCKED
  • Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的
  • 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程
  • synchronized 必须是进入同一个对象的 monitor 才有上述的效果
  • 不加 synchronized 的对象不会关联监视器,不遵从以上规则

synchronized原理

如下代码分析原理

static final Object lock=new Object();
static int counter = 0;
    
public static void main(String[] args) {
    synchronized (lock) {
        counter++;
    }
}

对应的字节码以及解释如下:

public static void main(java.lang.String[]);
 		descriptor: ([Ljava/lang/String;)V
 		flags: ACC_PUBLIC, ACC_STATIC
 		Code:
 		stack=2, locals=3, args_size=1
         0: getstatic #2 // <- lock引用 (synchronized开始)
         3: dup //  复制操作数栈栈顶的值放入栈顶,即复制了一份lock的引用
         4: astore_1 //  操作数栈栈顶的值弹出,将lock的引用存到局部变量表中
         5: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针,这时候关联了monitor,就会把hashcode这些都换成39字节的monitor
         6: getstatic #3 // <- i
         9: iconst_1 // 准备常数 1
         10: iadd // +1
         11: putstatic #3 // -> i
         14: aload_1 //  从局部变量表中取得lock的引用,放入操作数栈栈顶
         15: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList
         16: goto 24
         19: astore_2 // e -> slot 2
         20: aload_1 // <- lock引用
         21: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList
         22: aload_2 // <- slot 2 (e)
         23: athrow // throw e
         24: return
         Exception table: // 出现错误执行的行数也包括monitorexit,表示遇到错误会自动释放锁
	         from to target type
	         6    16  19   any
	         19   22  19   any
         LineNumberTable:
	         line 8: 0
	         line 9: 6
	         line 10: 14
	         line 11: 24
         LocalVariableTable:
	         Start Length Slot Name Signature
	         0      25     0   args [Ljava/lang/String;
         StackMapTable: number_of_entries = 2
         	frame_type = 255 /* full_frame */
         		offset_delta = 19
         		locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
         		stack = [ class java/lang/Throwable ]
         frame_type = 250 /* chop */
         		offset_delta = 4
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值