Synchronized

网友投稿 508 2022-05-30

我们知道 Synchronized 是 Java 中解决并发问题的一种最常用的方法, 也是最简单的一种方法. 被也被称为内置锁.

Synchronized 的作用主要有三个:

确保线程互斥的访问同步代码

保证共享变量的修改能够及时可见

有效解决重排序问题。

从语法上讲, Synchronized 总共有三种用法:

修饰普通方法, 锁是当前实例对象.

修饰静态方法, 锁是当前类的 class 对象.

修饰代码块, 锁是括号中的对象.

关于使用方式, 这里就不再进行一一描述了. 我们直接进入正题, 看 Synchronized 的底层实现原理是什么.

1. Synchronized 原理

首先, 我们先来看一段代码, 使用了同步代码块和同步方法, 通过使用 javap 工具查看生成的 class 文件信息来分析 synchronized 关键字的实现细节.

代码片段

public static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic 3: dup 4: astore_1 5: monitorenter //---------------------------------------------1. 6: aload_1 7: monitorexit //---------------------------------------------2. 8: goto 16 11: astore_2 12: aload_1 13: monitorexit //---------------------------------------------3. 14: aload_2 15: athrow 16: return ... public static synchronized void test(); descriptor: ()V flags: (0x0029) ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //---------------------------------------------4. Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 21: 0

从生产的 class 信息中, 可以清楚的看到两部分内容

同步代码块中使用了 monitorenter 与 monitorexit 指令.

同步方法中依靠方法修饰符 flags 上的 ACC_SYNCHRONIZED 实现.

先看反编译出 main 方法中标记的 1 与 2. monitorenter / monitorexit 关于这两条指令的作用, 参考 JVM 中对他们的描述如下:

monitorenter

每个对象有一个监视器锁 monitor, 当 monitor 被占用时就会处于锁定状态, 线程执行 monitorenter 指令时尝试获取 monitor 的所有权, 过程如下

如果 monitor 的进入数为 0 , 则该线程进入 monitor, 然后将进入数设置为 1, 该线程即为 monitor 的拥有者.

如果线程已经占有该 monitor, 只是重新进入, 则进入 monitor 的进入数加 1.

如果其他线程已经占用了 monitor, 则该线程进入阻塞状态, 直到 monitor 的进入数为 0, 再尝试获取 monitor 的所有权.

Synchronized

monitorexit

执行 monitorexit 的线程必须是对应 monitor的所有者. 执行指令时, monitor的进入数减 1. 如果减 1 后进入数为 0, 则线程退出 monitor. 不再是这个 monitor 的所有者. 其他被这个 monitor 阻塞的线程可以尝试去获取这个 monitor 的所有权.

monitorenter 指令是在编译后插入到同步代码块开始的位置, 而 monitorexit 是插入到方法的结束处和异常处. 这也就是为什么在 3 处会单独有一个 monitorexit 了.

ACC_SYNCHRONIZED

当方法调用时, 调用指令将检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置, 如果设置了, 执行线程将先获取 monitor, 获取成功之后才能执行方法体. 方法执行完后再释放 monitor, 在方法执行期间, 其他任何线程都无法再获得同一个 monitor 对象.

其实这个和上面 monitorenter 与 monitorexit 本质上没有区别, 只是方法的同步是一种隐式的方式来实现的, 无需通过字节码来完成.

看完这些, 是不是觉得有点和 AQS 中的 state 相似? 如果看完了 从 LockSupport 到 AQS 的简单学习 这篇文章的朋友, 再来看这里, 我相信应该会很容易理解.

任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring编程式事务详解
下一篇:HDFS分布式文件系统学习(3)
相关文章