synchronized原理


参考博客: https://blog.csdn.net/asiaLIYAZHOU/article/details/76098575
synchronized是Java中实现进程同步最简单的一种方式.
synchronized的三个作用

  • 保证互斥性(即:原子性)
  • 保证可见性
  • 保证顺序性

synchronized的三种常用方法

  • 修饰普通方法
  • 修饰静态方法
  • 修饰代码块

1、synchronized实现原理(修饰代码块)

先看下面代码:

public class SynchronizedDemo {
     public void method() {
         synchronized (this) {
             System.out.println("Method 1 start");
         }
     }
 } 

上述代码对应的字节码
在这里插入图片描述
可以看到synchronized所覆盖的代码块的首尾部分分别加上了monitorenter和monitorexit。JVM中对于上述两条指令的解释如下:

  • monitorenter
Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: 
• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. 
• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. 
• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

大意就是: 每一个对象有一个相关联的监视器(monitor,用于监视该类的进入数),当monitor为被占用时便被锁定住。当执行monitorenter执行时,当前线程尝试获取monitor.

  1. 如果当前monitor的进入数为0,则说明monitor未被占用,则当前线程占有该monitor,同时monitor的进入数变为1
  2. 如果当前monitor进入数不为0,且monitor的占用者是当前线程,则可以重复占用该monitor,同时进入数加1
  3. 如果当前monitor进入数不为0,且monitor的占用者是其他线程,则线程进入阻塞状态,直到monitor的进入数重新变为0,再重新尝试获取所有权
  • monitorexit:
The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. 
The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so. 

大意就是:执行monitorexit的线程必须是objectref所对应的monitor的所有者。

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

通过上述两段描述便不难理解synchronized的实现原理了。synchronized实际是通过关联一个对象的monitor来实现的。在前文的代码demo中,关联的对象实际就是synchonizedDemo实例对象的monitor。其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

monitor通过计数器,实现了synchronized的可重入功能,需要等到该线程的所有对目标的锁都释放后,其他线程才能竞争到该锁。

2、synchronized修饰方法

public class SynchronizedMethod {
     public synchronized void method() {
         System.out.println("Hello World!");
     }
 }

对应字节码
在这里插入图片描述
从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

3、synchronized修饰不同方法,获取的monitor对象:

修饰对象方法:此时获取的monitor为实例对象(即this)的monitor。锁住的是某个对象。
修饰类方法:此时获取的monitor为类class对象(如Synchronized.class)的monitor。锁住的是用该类创建的所有对象。

4、可见性和有序性

通过上述解释,已经知道synchronized是如何保证互斥性的,那么其是怎么实现可见性和顺序性的呢?其实也跟monitor相关:

  • 可见性:执行到monitorenter时,线程会重新从主内存中将数据同步到本地工作内存,从而保证其可以看到其他线程的修改。同时执行monitorexit时,线程也会将本地工作内存的数据同步到主内存中
  • 有序性:monitorenter、monitorexit修饰的代码将禁止进行重排序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城有术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值