java锁状态基本概念

Updated on in 程序人生 with 0 views and 0 comments

1、简介

Java有四种锁状态:无锁、偏向锁、轻量锁、重量锁,锁可以逐渐升级,但是不可以降级。

2、锁状态

在Java中对象的锁存储在对象头Mark Word中(该字段存储对象的hashcode或者锁信息)

2.1、偏向锁

2.1.1、获取

当一个线程访问同步代码块并获取锁的时候,会在对象头和栈帧中的锁记录里存储偏向锁的线程ID,以后该线程在进入和退出同步块时,不需要进行CAS加锁和解锁,会查询对象头中Mark Word是否存储着指向该线程的偏向锁,如果成功,表示线程已经获得了锁。如果失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程;

2.1.2、撤销

当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放偏向锁。当另一个线程试图访问偏向锁时,会首先暂停拥有偏向锁的进程,如果线程没有运行,则将对象锁的对象头设置为无锁状态。如果线程正在运行,则会暂停该线程,恢复到无锁状态,或者标记对象不适合作为偏向锁,最后唤醒该线程。

2.1.3、优缺点

偏向锁适用于大多数时候只有一个线程访问同步块竞争锁的情况,可以提升该线程获取锁的速度。

在有多个线程调用这个偏向锁的时候,会带来额外的锁撤销的资源开销。

2.2、轻量锁

2.2.1、获取

线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。

2.2.2、撤销

轻量级解锁时,会使用原子的CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

image.png

2.2.3、优缺点

竞争的线程不会阻塞,线程相应速度提升;

如果竞争锁失败,自旋会消耗资源。

2.3、重量锁synchronized

Java中的每一个对象都可以作为锁。
对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象。

2.3.1、锁的获取和撤销

从JVM规范中可以看到Synchonized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,细节在JVM规范里并没有详细说明。但是,方法的同步同样可以使用这两个指令来实现。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

2.3.2、优缺点

竞争不使用自旋,cpu消耗少;

线程阻塞后响应慢。


标题:java锁状态基本概念
作者:wenyl
地址:http://www.wenyoulong.com/articles/2021/01/12/1610425997432.html