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

相关推荐
轻口味35 分钟前
命名空间与模块化概述
开发语言·前端·javascript
晓纪同学2 小时前
QT-简单视觉框架代码
开发语言·qt
威桑2 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服2 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans2 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
码农君莫笑2 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
Chinese Red Guest2 小时前
python
开发语言·python·pygame