java 对象的结构以及Synchronized锁实现原理以及优化

对象是由 对象头、实例数据、填充数据组成

其中对象头是我们今天着重强调的!!!

java 对象的结构以及Synchronized锁实现原理以及优化_第1张图片

 对象头由 这几部分组成,其中最后面的长度是数组才有的!!

mark Word由什么组成呢?

java 对象的结构以及Synchronized锁实现原理以及优化_第2张图片

 分别对于正常状态 、偏向锁、轻量锁、重量级锁、被Gc后

它们是由状态码来进行标识

当给对象上了重量级锁的时候,对象的Markword当中标识码是10,然后还有一个指针是指向与该对象关联的监视器对象

什么是监视器呢,其实它是由c++编写的,结构如下

java 对象的结构以及Synchronized锁实现原理以及优化_第3张图片

 初始状态下,Monitor中的Owner为Null;
当Thread-2执行Synchronized(obj)加锁操作时,会首先检查Owner是否为Null,如果为Null,则将Owner置为Thread-2;
在Thread-2持有锁的过程中,如果有Thread-3,Thread-4,Thread-5执行加锁操作,也会先判断是Monitor的Owner是否为Null;发现Owner已经为Thread-2了,则进入EntryList中进入阻塞状态;
Thread-2释放锁后,会唤醒EntryList中的等待线程,让它们竞争锁。

_WaitSet和 _EntryList有什么区别呢?

​ 当多个线程同时访问同步代码时,首先进入的就是_EntryList 。当获得对象的monitor时,_owner 指向当前线程,_count进行加1。
​ 若持有monitor的线程调用wait() ,则释放持有的monitor ,_owner 变为null ,_count 减1。
​ 同时该线程进入_WaitSet 等待被唤醒。如果执行完毕,也释放monitor。

java 对象的结构以及Synchronized锁实现原理以及优化_第4张图片

wait/notify使用

常用方法

  • obj.wait() 让当前持有锁的Owner进入Monitor的WaitSet进行等待。
  • obj.wait(long timeout) 让进入 object 监视器的线程到 waitSet 等待,可制定等待超时时间。
  • obj.notify() Owner从正在 waitSet 等待的线程中挑一个唤醒。
  • obj.notifyAll() Owner让正在 waitSet 等待的线程全部唤醒。

以上方法都是Object类的方法,必须要获取该对象锁,才能调用上述方法。

消除锁是虚拟机另外一种锁的优化,这种优化更彻底,在JIT编译时,对运行上下文进行扫描,做逃逸分析,去除不可能存在竞争的锁(去掉了申请和释放锁的代码了)。比如下面代码的method1和method2的执行效率是一样的,因为object锁是私有变量,不存在所得竞争关系。

java 对象的结构以及Synchronized锁实现原理以及优化_第5张图片

锁粗化是虚拟机对另一种极端情况的优化处理,通过扩大锁的范围,避免反复获取锁和释放锁。比如下面method3经过锁粗化优化之后就和method4执行效率一样了。

java 对象的结构以及Synchronized锁实现原理以及优化_第6张图片

锁的优化,可以从偏向锁逐渐膨胀到重量级锁
 

java 对象的结构以及Synchronized锁实现原理以及优化_第7张图片

偏向锁:线程获取锁后,锁对象的Mark Word标记偏向锁,通过一个字段记录当前线程id,

  1. 本线程再次争取锁时:检查这个线程ID跟自己一样,就重入。
  2. 不同的线程争取锁:锁对象中的线程ID不是自己,且有偏向锁标识,则发起偏向锁取消操作。
    • 在SafePoint的时候,若偏向锁取消成功,且当前线程通过CAS操作争取到了锁,则继续保持偏向锁状态.
    • 若一次CAS操作未争取到锁,意味着还有其他的线程也在竞争这个锁,此时就进行锁升级,升级为轻量级锁。
  3. 轻量级锁是自适应自旋锁
    • 自旋获取锁成功:保持轻量级锁状态??
    • 自旋获取锁失败 ,则进入重量级锁;
  • 偏向锁:一次CAS操作,修改一下锁中的字段,就被标识为拿得到了锁
  • 轻量锁:一次CAS操作拿不到锁,,那就自旋空转多次CAS操作,会稍稍费一点CPU,但是能更快的拿到锁;自适应自旋后,还拿不到锁,那就只能使用重量级锁了。
    • 自旋锁:许多情况下,共享数据的锁定状态持续时间较短,切换线程不值得,通过让线程执行循环等待锁的释放,不让出CPU。如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式。但是它也存在缺点:如果锁被其他线程长时间占用,一直不释放CPU,会带来许多的性能开销。
    • 自适应自旋锁:这种相当于是对上面自旋锁优化方式的进一步优化,它的自旋的次数不再固定,其自旋的次数由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定,这就解决了自旋锁带来的缺点。

 

你可能感兴趣的:(java,jvm,开发语言)