锁机制

159 阅读3分钟

一丶Monitor

1、什么是Monitor?

被翻译为监视器或者管程

每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark World就被设置指向Monitor对象的指针。

Monitor结构如下:

image.png

  1. 线程2访问同步代码块synchronized(obj)
  2. obj的Mark World指向Monitor,Monitor的owner指向线程2
  3. 线程345来synchronized(obj)的时候,发现owner有人了,就进入blocked状态,等待
  4. 线程2执行完,owner为空,通知EntryList内的线程去竞争(非公平:先进来的不见得先执行)
  5. 竞争到的线程再变成为owner
  6. 线程01是获得过锁不满足执条件的线程

注意:同一个对象才会关联到同一个Monitor

2、源码解析

等有时间写一下源码分析

二、锁

1、重量级锁

重量级锁就是Monitor

2、轻量级锁

1.创建锁记录对象(Lock Record),每个线程的栈帧都会包含一个锁记录结构,内部可以存储锁定对象的Mark World

image.png

2.让锁记录中的Object reference指向锁对象,并尝试用cas替换Object的Mark Word,将Mark Word的值存在锁记录(红色箭头代表交换;01表示无锁;00表示轻量级锁)

image.png

3.如果CAS替换成功,对象头存放了锁记录地址和状态00,表示该线程给对象加锁

image.png

4.如果CAS失败有两种情况

  1. 如果其他线程已经持有该obj的轻量级锁,就会锁膨胀
  2. 如果时自己执行synchronized锁重入,那么再添加一条Lock Record作为重入的计数

image.png

5.退出synchronized的时候如果有null表示有锁重入,清除之后计数-1

image.png

6.退出synchronized,当锁记录值不为null,这时使用CAS把markword的值恢复给对象头

  • 成功:解锁
  • 失败:说明锁成了重量级锁,进入重量级锁解锁流程

3、锁膨胀

如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其他线程为此对象加上轻量级锁(有竞争),这时需要进行锁膨胀,把轻量级锁变为重量级锁

  1. 当线程1进行轻量级加锁时,线程0已经对该对象加了轻量级锁

image.png

  1. 这时线程1加轻量级锁失败,进入锁膨胀流程
    • 即为Object对象申请Monitor锁,让Object指向重量级锁地址(重量级锁10)
    • 然后自己进入Monitor的EntryList BLOCKED

image.png|

  1. 线程0解锁,使用CAS将Mark Word的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照Monitor地址找到Monitor对象,设置Owner为null,唤醒EntryList中Blocked的线程

4、自旋优化

image.png image.png image.png

5、偏向锁

5.1、定义

image.png

image.png image.png

image.png

5.2、偏向锁撤销

1、调用对象的hashCode()

image.png

2、线程1加锁,释放,然后线程2加锁发现已经属于线程1,会给线程加一个轻量级锁,用完后释放

3、调用wait/notify

4、批量重新偏向:线程1执行很多次一直加锁解锁,都是偏向锁,线程2加轻量级锁,解锁之后都是轻量级锁,当到达一个阈值(20)之后,可以重新偏向给线程2

5、批量撤销

image.png

5.3、锁消除

对象根本不会被其他线程操作的时候,jit会进行优化,把锁去掉,可以加参数取消掉锁消除