
synchronized是在软件层面依赖于JVM,而j.u.c下的lock是依赖于硬件层面。
1> 如果synchronized修饰的是对象,那么它是依赖于monitor对象来实现锁的机制的。代码如下所示:
public class SynchronizeTest {public void method(){synchronized (this) {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "+++++++" + i);}}}
}
先编译上述文件:javac -encoding utf-8 类名.java
反编译类文件: javap -c 类名.class
反编译后可以看到如下截图:
编译结果解析:
monitorexit指令出现了两次,第1次为同步正常退出释放锁;第2次为发生异步退出释放锁;
通过上面两段描述,我们应该能很清楚的看出Synchronized的实现原理,
Synchronized的语义底层是通过一个monitor的对象来完成,
其实wait/notify等方法也依赖于monitor对象,
这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,
否则会抛出java.lang.IllegalMonitorStateException的异常的原因。
2> 如果synchronized修饰的是方法,那么则是通过ACC_SYNCHRONIZED 标示符来进行加锁的。代码如下所示:
public class SynchronizeTest {public synchronized void method() {for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "+++++++" + i);}}
}
反编译如下:
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 标志符是否被设置,如果设置了,线程将会先获取monitor,获取成功之后才能执行方法体,方法执行完之后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。
两种同步方式本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。两个指令的执行是JVM通过调用操作系统的互斥原语mutex来实现,被阻塞的线程会被挂起、等待重新调度,会导致“用户态和内核态”两个态之间来回切换,对性能有较大影响。
无论是实例对象还是类对象。在JVM中,每个对象都是由三部分组成的:对象头、实例数据、数据填充。synchronized的锁的信息都是存储在对象头里。对象组成结构如下:
Synchronized用的锁就是存在Java对象头里的,那么什么是Java对象头呢?Hotspot虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)、Class Pointer(类型指针)。其中 Class Pointer是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。 Java对象头具体结构描述如下:
其中Mark Word在默认情况下存储着对象的哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等,以下是32位JVM的Mark Word默认存储结构:
对象头信息是与对象自身定义的数据无关的额外存储成本,但是考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据,它会根据对象的状态复用自己的存储空间,也就是说,Mark Word会随着程序的运行发生变化,可能变化为存储以下4种数据:
synchronized属于对象锁,而任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态。在Java虚拟机(HotSpot)中,Monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的):
ObjectMonitor() {_header = NULL;_count = 0; //记录个数_waiters = 0,_recursions = 0;_object = NULL;_owner = NULL;_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet_WaitSetLock = 0 ;_Responsible = NULL ;_succ = NULL ;_cxq = NULL ;FreeNext = NULL ;_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表_SpinFreq = 0 ;_SpinClock = 0 ;OwnerIsThread = 0 ;
结构中有几个重要的字段,_count、_owner、_EntryList、_WaitSet。
当多个线程访问同步代码块时:
1> 首先线程会进入EntryList集合,然后当线程拿到Monitor对象时,进入owner区域,并把Monitor的owner设置为当前线程,_owner指向持有ObjectMonitor对象的线程,并把计数器count加1.
2> 若线程调用wait方法,将释放当前持有的monitor对象,同时owner变量恢复为null,count自减1,同时该线程进入WaitSet集合中等待被唤醒;
3> 当前线程执行完毕,也将释放monitor(锁)并复位count的值,以便其他线程进入获取monitor(锁);
过程如下图所示:
Synchronized与等待唤醒:
Synchronized的可重入与中断:
/*** Interrupt设置一个线程为中断状态* Interrupt操作的线程处于sleep,wait,join 阻塞等状态的时候,清除“中断”状态,抛出一个InterruptedException* Interrupt操作的线程在可中断通道上因调用某个阻塞的 I/O 操作(serverSocketChannel. accept()、t、socketChannel.open、 * ad、socketChannel.write、ad、fileChannel.write),会抛出一个ClosedByInterruptException**/
public void interrupt();
/*** 判断线程是否处于“中断”状态,然后将“中断”状态清除**/
public static boolean interrupted();
/*** 判断线程是否处于“中断”状态**/
public boolean isInterrupted();
在实际使用中,当线程正处于调用sleep、wait、join方法后,调用interrupt会清除线程中断状态,并抛出异常。而当线程已进入临界区、正在执行,则需要isInterrupted()或interrupted()与interrupt()配合使用中断执行中的线程。
Sychronized修饰的方法、代码块被多个线程请求时,调用中断,正在执行的线程响应中断,正在阻塞的线程、执行中的线程都会标记中断状态,但阻塞的线程不会立刻处理中断,而是在进入临界区后再响应。
1>锁的升级只能从低到高,不能从高到低。
锁的升级过程如下:
2>锁标志位变化:
3>锁消除:因为Synchronized锁的是对象,如果每一个线程都锁一个新的对象,那么这个时候就不需要进行上锁了,就是所谓的锁消除,锁消除的底层实现原理是JVM的逃逸分析原理。如以下代码所示:
synchronized (new Object()){System.out.println("开始处理逻辑");}
4>锁粗化:
把很多次锁的请求合并成一个请求,以降低短时间内大量锁请求、同步、释放带来的性能损耗。
StringBuffer sb = new StringBuffer();public void lockCoarseningMethod() {synchronized (Test.class) {sb.append("1");}synchronized (Test.class) {sb.append("2");}synchronized (Test.class) {sb.append("3");}synchronized (Test.class) {sb.append("4");}
}
锁粗化后:
StringBuffer sb = new StringBuffer();public void lockCoarseningMethod() {synchronized (Test.class) {sb.append("1");sb.append("2");sb.append("3");sb.append("4");}
}
欢迎各位关注我的JAVAERS公众号,陪你一起学习,一起成长,一起分享JAVA路上的诗和远方。在公众号里面都是JAVA这个世界的朋友,公众号每天会有技术类文章,面经干货,也有进阶架构的电子书籍,如Spring实战、SpringBoot实战、高性能MySQL、深入理解JVM、RabbitMQ实战、Redis设计与实现等等一些高质量书籍,关注公众号即可领取哦。
本文发布于:2024-03-12 13:55:43,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/1710646160144619.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
| 留言与评论(共有 0 条评论) |