前言:在慕课网上学习剑指Java面试-Offer直通车时所做的笔记,供本人复习之用,比较难,我也没大懂,只记录大概意思以后有接触了再改,想要详细解说的不建议看这篇博客.
目录
第五章 Synchronized和ReentrantLock的区别
第一章 对象在内存中的布局
HotSpot 对象在内存中的布局分为三块区域,对象头,实例数据,对齐填充,我们在这里主要讲解对象头.
synchronized使用的锁对象是存储在java对象头里的,其主要结构是由Mark Word和Class Metadata Address组成,Class Metadata Address是指向类元数据的指针,虚拟机通过这个指针确认其是哪个对象的实例,Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键.
Mark Word:
第二章 Monitor
上面介绍了java对象头,下面我们介绍Monitor.
Monitor:每个对象天生自带了一把看不见的锁,叫做内部锁或者Monitor锁.
Monitor也称为管程或者监视器锁,我们可以把它理解为一个同步工具,也可以描述为一种同步机制,通常它被描述为一个对象.
这里我们拿重量级锁进行分析,锁的标识位为10,指针指向的是monitor对象的起始地址,每个对象都存在着一个monitor与之关联,monitor存在于对象的对象头中,对象与monitor之间有多存在多种实现方式,如monitor可以与对象一起存在销毁,或当线程试图获取对象锁时,自动生成.当monitor被某个线程持有后,它便处于锁定状态.
在java虚拟机HotSpot中monitor由ObjectMonitor来实现,位于HotSpot虚拟机源码objectMonitor里,通过C++来实现.
ObjectMonitor中有两个队列,一个是WaitSet一个是EntryList,可以与等待池与锁池联系起来,他们就是用来保存ObjectWaiter的对象列表,每个对象锁的线程都会被封装成ObjectWaiter来保存到里面,owner指向持有ObjectMonitor的线程.
当多个线程访问同一段同步代码的时候,首先会进入到EntryList集合中,当线程获取到对象的Monitor之后,就进入到Object区域,并把Monitor中的Owner变量设置为当前线程,同时Monitor中的count就会加1,如果线程调用wait方法将会释放当前持有的Monitor,owner会被恢复成null,count也会被减1,同时该线程ObjectWaiter实例就会进入到waitSet集合等待被唤醒,若当前线程执行完毕,它也将释放monitor锁,并复位对应变量的值,以便其它线程进入获取monitor锁.
Monitor锁的竞争,获取与释放.
2.1 Monitor在字节码中的表示
java代码:
对应的字节码:
syncsTask方法字节代码:
可以分析出,同步语句块的实现依赖的是monitorenter与monitorexit指令.
monitorenter指向代码块的开始位置,首先获取printStream这个类,传入参数,执行方法.
monitorexit指明同步代码块的结束位置.
当执行monitorenter指令时,当前线程将试图获取对象锁即Object(没听清,音译)所对应的持有权,当Object的Monitor进入计数器的count为0时,线程就可以成功的获取到Monitor,并将计数器设置为1表示取锁成功,如果我们当前线程在之前已经拥有了objectMonitor的持有权,它可以重入这个monitor.假如其它线程已经先于当前线程拥有ObjectMonitor的所有权,那么当前线程将会被阻塞在这里,直到持有该锁的线程执行完毕,即monitorexit被执行,执行线程将释放Monitor锁,并设置计数器为0,其它线程将有机会持有Monitor.
为了保证该方法异常时也能正确执行,编译器会自动生成一个异常处理器,包含另一个monitorexit方法.
什么是重入:
重入代码如下: