synchronized的实现原理

利用synchronized实现同步的基础:java中的每一个对象都可以作为锁,synchronized用的锁是存在java对象头里的,具体表现为以下三种形式。

  (1)对于普通同步方法,锁是当前实例对象。

  (2)对于静态同步方法,锁是当前类的Class对象。

  (3)对于同步方法块,锁是synchronized括号例配置的对象。

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或者抛出异常时必须释放锁。synchonized在JVM里的实现原理:JVM基于进入和退出Monitor对象来实现方法和代码块同步。同步代码块是使用monitorenter和monitorexit指令实现的,JVM保证每个monitorenter必须要有对应的monitorexit与之匹配。


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 的所有权。


任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

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

javaSE中,锁一共有四种状态:无锁,偏向锁,轻量级锁,重量级锁

1.偏向锁

当一个线程访问同步代码块时并获取锁时,会在对象头和栈帧的锁记录里存储锁偏向的线程ID,当该线程再次进入和退出同步代码块时不需要进行CAS操作来加锁和解锁,只需确认对象头中的Mark Word是否存储着指向当前线程的偏向锁。若有,则该线程已经获得锁;若没有,则需测试Mark Word中偏向锁的位置标识是否为1(表示当前有偏向锁),若没有设置,则使用CAS竞争锁,若设置,则尝试使用CAS将对象头的偏向锁指向当前线程。

1)偏向锁的撤销

只有当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。撤销需要等到全局安全点(没有正在执行的字节码)。首先暂停拥有偏向锁的线程,检查该线程是否存活,若线程不处于活动状态,则将对象头设置为无锁状态;若线程存活,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和Mark Word要么偏向于其他线程,要么恢复到无所状态,最后唤醒暂停的线程。

2)关闭偏向锁

通过JVM中的参数关闭偏向锁,UseBiasedLocking=false,那么程序会默认进入轻量级锁。

2.轻量级锁

1)轻量级锁加锁

线程执行同步代码块之前,JVM会在当前线程的栈帧中创建用于存储锁记录的空间(Displaced Mark Word),并将对象头Mark Word复制到锁记录中。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针,若成功,则获得锁,若失败,则表示其他线程获得锁,当前线程则尝试使用自旋来获取锁(自旋会消耗CPU)。

2)轻量级锁解锁

解锁时,会使用CAS将Displaced Mark Word替回到Mark Word中,若成功,则表示没有发生竞争,若失败,则表示当前锁存在竞争,锁会膨胀为重量级锁。当锁是重量级锁时,其他线程试图获取锁时,都会被阻塞。只有当锁释放之后,会唤醒这些线程,然后这些线程再来竞争锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值