认识 synchronized
对于写多线程程序的人来说, 经常碰到的就是并发问题, 对于容易出现并发问题的地方加上 synchronized 修饰符基本上就搞定 了, 如果说不考虑性能问题的话, 这一招绝对能应对百分之九十以上的情况, 若对于性能方面有要求的话就需要额外的知识比如读写锁等等本文目的先了解透彻 synchronized 的基本原理
Synchronized 的基本使用
Synchronized 的作用主要有三个:
(1)确保线程互斥的访问同步代码
(2)保证共享变量的修改能够及时可见
(3)有效解决重排序问题
从语法上讲, Synchronized 总共有三种用法:
(1)修饰普通方法
(2)修饰静态方法
(3)修饰代码块
- package com.paddx.test.concurrent;
- public class SynchronizedDemo {
- public void method() {
- synchronized (this) {
- System.out.println("Method 1 start");
- }
- }
- }
对于上述方法我们很容易就知道是线程安全的, 具体是怎么做的到的线程安全呢, 对 class 通过 javap 编译结果如下:
monitorenter
每个对象有一个监视器锁 (monitor) 当 monitor 被占用时就会处于锁定状态, 线程执行 monitorenter 指令时尝试获取 monitor 的所有权, 过程如下:
1 如果 monitor 的进入数为 0, 则该线程进入 monitor, 然后将进入数设置为 1, 该线程即为 monitor 的所有者
2 如果线程已经占有该 monitor, 只是重新进入, 则进入 monitor 的进入数加 1.
3. 如果其他线程已经占用了 monitor, 则该线程进入阻塞状态, 直到 monitor 的进入数为 0, 再重新尝试获取 monitor 的所有权
monitorexit
执行 monitorexit 的线程必须是 objectref 所对应的 monitor 的所有者
指令执行时, monitor 的进入数减 1, 如果减 1 后进入数为 0, 那线程退出 monitor, 不再是这个 monitor 的所有者其他被这个 monitor 阻塞的线程可以尝试去获取这个 monitor 的所有权
通过这两段描述, 我们应该能很清楚的看出 Synchronized 的实现原理, Synchronized 的语义底层是通过一个 monitor 的对象来完成, 其实 wait/notify 等方法也依赖于 monitor 对象, 这就是为什么只有在同步的块或者方法中才能调用 wait/notify 等方法, 否则会抛出 java.lang.IllegalMonitorStateException 的异常的原因
原理总结
每个对象都有一个内部的锁或者叫做是监视器, 称之为 monitor, 当一个方法加上 synchronized 关键字的时候, 如果一个线程想执行这个方法那么首先需要获取这个对象的 monirot 权限, 对应到指令上面也就是需要获取 monitorenter 指令, 如果一个对象获取到这个指令之后, 那么 monitor 的进入数为 1, 当其他线程再次获取的时候发现这个对象的 monitor 对象被别的线程所占用, 那么进入阻塞状态, 知道占用这个对象的线程执行 monitorexit, 设置进入数为 0 为止
如果 synchronized 加在普通方法上, 那么有效的范围是多个线程执行同一个对象的方法通过上面的解释应该比较容易理解了, 因为不同的对象获取的是不同的 monitor 监视器, 自然也就不存在占用等待的过程了如果是加载 static 方法上那么需要获取的就是这个对象所在 class 的 Class 对象, 所以此时不管是几个对象, 对应的都是同一个 class 对象, 也就是说多个线程又存在对同一个 monitor 的占用等待的过程了所以说加载 static 上是对于整个类文件有效
来源: https://www.cnblogs.com/jurendage/p/8691975.html