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

相关推荐
重生之我在20年代敲代码2 分钟前
strncpy函数的使用和模拟实现
c语言·开发语言·c++·经验分享·笔记
爱上语文4 分钟前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
编程零零七2 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
2401_858286113 小时前
52.【C语言】 字符函数和字符串函数(strcat函数)
c语言·开发语言
铁松溜达py3 小时前
编译器/工具链环境:GCC vs LLVM/Clang,MSVCRT vs UCRT
开发语言·网络
everyStudy3 小时前
JavaScript如何判断输入的是空格
开发语言·javascript·ecmascript
C-SDN花园GGbond5 小时前
【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)
c语言·开发语言·数据结构·排序算法
迷迭所归处6 小时前
C++ —— 关于vector
开发语言·c++·算法
架构文摘JGWZ6 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
leon6256 小时前
优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序
开发语言·算法·matlab