Java 多线程之 Monitor (十一)

前言

在后续会深入分析 synchronized 的底层原理,但在说之前必须得讲解 Monitor 做位铺垫。

正文

Java 对象头

以 32 位 Java 虚拟机(JVM)为例子

普通对象 (64 位 或 8 个字节)

|--------------------------------------------------------------|
|                     Object Header (64 bits)                  |
|------------------------------------|-------------------------|
|        Mark Word (32 bits)         |    Klass Word (32 bits) |
|------------------------------------|-------------------------|

PS : Klass Word 可以理解为一个指针,指向了从属对象的 Class,找到它的类对象

数组对象(96位)

|---------------------------------------------------------------------------------|
|                                 Object Header (96 bits)                         |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |    Klass Word(32bits) |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|

Mark Word 对象头结构

|-------------------------------------------------------|--------------------|
|                  Mark Word (32 bits)                  |       State        |
|-------------------------------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:0 | lock:01|       Normal       |
|-------------------------------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:01|       Biased       |
|-------------------------------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:00| Lightweight Locked |
|-------------------------------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:10| Heavyweight Locked |
|-------------------------------------------------------|--------------------|
|                                              | lock:11|    Marked for GC   |
|-------------------------------------------------------|--------------------|

注意:32 位 JVM 中 Mark Word 是 32位,但在 64 位的 JVM 中,则 Mark Word 为 64 位。


State
  • Normal:正常状态 。

  • Lightweight Locked : 轻量级锁

  • Heavyweight Locked : 重量级锁

  • Marked for GC : 被垃圾回收


Mark Word
  1. lock : 2位的锁状态标记位

  2. biased_lock :(加了偏向锁) 对象是否启用偏向锁标记,只占1个二进制位。为1时表示对象启用偏向锁,为0时表示对象没有偏向锁。

  3. age :4位的Java对象年龄。在 GC 中,如果对象在Survivor区复制一次,年龄增加1。当对象达到设定的阈值时,将会晋升到老年代。默认情况下,并行GC的年龄阈值为15,并发GC的年龄阈值为6。由于age只有4位,所以最大值为15,这就是-XX:MaxTenuringThreshold选项最大值为15的原因。

  4. identity_hashcode :25位的对象标识Hash码,采用延迟加载技术。调用方法 System.identityHashCode() 计算,并会将结果写到该对象头中。当对象被锁定时,该值会移动到管程 Monitor 中。

  5. thread :持有偏向锁的线程ID

  6. epoch :偏向时间戳

  7. ptr_to_lock_record :指向栈中锁记录的指针

  8. ptr_to_heavyweight_monitor :指向管程 Monitor 的指针

举个例子,在 32 位 JVM 中,一个 Integer 要占 8 个字节作为对象头 + 4 个字节做为存储的Value的。

而一个 int 只需要 4 个字节即可。

也就是说,一个 Integer 比 int 大 3 倍,在内存敏感的地方,建议用 int 类型,而非包装类。


Monitor工作原理

Monitor 又称为监视器管程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9iGP2xl3-1608306169220)(C:\Users\007\AppData\Roaming\Typora\typora-user-images\1608214001121.png)]

  • Monitor 是操作系统提供的,刚开始时,其中的 Owner 为 null

  • Thread-1 执行 synchronized(obj) 时会将 Monitor 的所有者 Owner 置为 Thread -1,Monitor 中只能有一个 Owner

  • 在 Thread -1 上锁的过程中,如果 Thread -3 、Thread -4 也执行 synchronized(obj) ,就会进入 EnrtyList BLOCK (阻塞)

  • Thread -1 执行完毕同步代码块内容后,接着就唤醒 EnrtyList 中等待的线程来竞争锁,竞争时时非公平的

  • WaitSet 中的 Thread-0、 Thread-2 是之前获得过锁,但条件不满足而进入 WAITING 状态的线程。

注意:

  1. synchronized 必须是进入同一个对象的 Monitor 才有上述效果
  2. 不加 synchronized 的对象不会关联监视器,不遵从以上规则

synchronized 原理
 static final Object lock = new Object();
    static int counter = 0;

    public static void main(String[] args) {
        synchronized (lock){
            counter++;
        }
    }

IDEA 反编译后字节码

 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	
         4: astore_1						  // lock 引用存储到临时变量 slot 1 中,为了后续解锁用 
         5: monitorenter					  // 将lock对象MarkWord 置为 Monitor 指针
         6: getstatic     #3       			  // <- i           
         9: iconst_1						  // 准备常数 1				 
        10: iadd							  // +1
        11: putstatic     #3                  // -> i
        14: aload_1							  // <- lock 引用
        15: monitorexit						  // 将lock对象重置,唤醒 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: getstatic     #4                  // Field 
        27: getstatic     #3                  // Field counter:I
        30: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        33: return
      Exception table:
         from    to  target type
             6    16    19   any
            19    22    19   any
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值