JUC- Synchronized原理

对象头概念

以 32 位虚拟机为例

Klass Word:指向类对象的指针,标明这个对象的类型

普通对象

ruby 复制代码
|--------------------------------------------------------------|
|                    Object Header (64 bits)                   |
|------------------------------------|-------------------------|
|       Mark Word (32 bits)          |   Klass Word (32 bits)  |
|------------------------------------|-------------------------|

数组对象,对象头比普通对象多了数组长度

ruby 复制代码
|---------------------------------------------------------------------------------|
|                             Object Header (96 bits)                             |
|--------------------------------|-----------------------|------------------------|
|        Mark Word(32bits)       |   Klass Word(32bits)  |  array length(32bits)  |
|--------------------------------|-----------------------|------------------------|

其中 Mark Word 结构为,以Normal状态为例,会存储对象的hashcode和永久代年龄,是否是偏向锁,加锁状态。

32位

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

64位

ruby 复制代码
|--------------------------------------------------------------------|--------------------|
|                          Mark Word (64 bits)                       |        State       |
|--------------------------------------------------------------------|--------------------|
| unused:25 | hashcode:31 | unused:1 | age:4 | biased_lock:0 |  01   |        Normal      |
|--------------------------------------------------------------------|--------------------|
| thread:54 |   epoch:2   | unused:1 | age:4 | biased_lock:1 |  01   |        Biased      |
|--------------------------------------------------------------------|--------------------|
|                    ptr_to_lock_record:62                   |  00   | Lightweight Locked |
|--------------------------------------------------------------------|--------------------|
|                 ptr_to_heavyweight_monitor:62              |  10   | Heavyweight Locked |
|--------------------------------------------------------------------|--------------------|
|                                                            |  11   |    Marked for GC   |
|--------------------------------------------------------------------|--------------------|

原理之Monitor(锁)

Monitor 被翻译为监视器管程

每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针

Monitor 结构如下

  • WaitSet:
  • EntryList:等待锁释放的阻塞线程
  • Owner:指向拥有锁的线程
  • 刚开始 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 状态的线程,后面讲 wait-notify 时会分析

注意

  • synchronized 必须是进入**同一个对象的 monitor (即线程竞争的锁对象必须是相同的)**才有上述的效果

  • 不加 synchronized 的对象不会关联监视器,不遵从以上规则

原理之 synchronized

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

具体做法为:

  • 将 lock对象 MarkWord 置为 Monitor 指针
  • 将 lock对象 MarkWord 重置,重置为hashcode,分代年龄,锁状态,是否为偏向锁,这部分信息都在Monitor中存储,释放锁时,从Monitor中获取重置即可,同时唤醒EntryList中的阻塞线程竞争锁。
  • 同步代码块中出现异常,最后也会执行重置操作,并释放锁

对应的字节码为:

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对象 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:

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

相关推荐
dhxhsgrx9 分钟前
PYTHON训练营DAY25
java·开发语言·python
敲代码的 蜡笔小新3 小时前
【行为型之中介者模式】游戏开发实战——Unity复杂系统协调与通信架构的核心秘诀
unity·设计模式·c#·中介者模式
风逸hhh3 小时前
python打卡day25@浙大疏锦行
开发语言·python
刚入门的大一新生3 小时前
C++初阶-string类的模拟实现与改进
开发语言·c++
程序猿多布3 小时前
使用Visual Studio将C#程序发布为.exe文件
c#·visual studio
chxii4 小时前
5java集合框架
java·开发语言
老衲有点帅4 小时前
C#多线程Thread
开发语言·c#
C++ 老炮儿的技术栈4 小时前
什么是函数重载?为什么 C 不支持函数重载,而 C++能支持函数重载?
c语言·开发语言·c++·qt·算法
IsPrisoner5 小时前
Go语言安装proto并且使用gRPC服务(2025最新WINDOWS系统)
开发语言·后端·golang