Java锁的实现
Java锁的实现
在Java SE 1.6中,锁一共有4中状态,级别从低到高依次是:无锁、偏向锁、轻量级锁和重量级锁。
轻量级锁
轻量级锁是JDK 1.6之中加入的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统锁的机制就称为重量级锁。首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。要理解轻量级锁,必须先介绍虚拟机的对象内存布局中的对象头。
Java对象头
如果对象是数组类型,则虚拟机用3个字宽存储对象头,如果是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽等于4字节,即32bit。在64位虚拟机中,一字宽等于8字节,即64bit。
Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。32位JVM的Mark Word的默认存储结构如下
在运行期间,Mark Word里存储的数据会随着锁标志位和偏向锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据:
在64位虚拟机下,Mark Word是64bit大小的,其存储结构如下:
轻量级锁的加锁过程
在代码进入同步快的时候,如果此同步对象没有被锁定,(锁标志位为“01”状态),虚拟机首先将在当前线程的栈桢中建立一个锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝。
然后,虚拟将使用CAS操作尝试将对象的MarkWord更新为指向LockWord的指针,如果这个更新操作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位将转换为“00”,即表示此对象处于轻量级锁定状态
如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈桢(栈桢中的Lock Word),如果是说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块执行了。否则说明这个锁对象已经被其他线程占用了。如果有两条或两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,锁标志的状态值变为“10”,此时Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也就要进入阻塞状态。
轻量级锁的解锁过程
如果对象Mark Word仍然指向当前线程的锁记录(Lock Record),就用CAS操作把对象的Mark Word用当前线程的LockRecord(加锁之前Mark Word的拷贝)进行替换,如果替换成功,整个同步过程就完成了。如果替换失败,说明有其他线程尝试获取该锁,锁就会膨胀为重量级锁
一旦锁升级为重量级锁,就不再恢复到轻量级锁状态。在重量级锁状态下,其他线程试图获取锁时,都会被阻塞,当持有锁的线程释放锁后会唤醒这些线程,被唤醒的线程就会进行新一轮的锁竞争。
轻量级锁能提升程序同步性能的依据是“对于绝大部分的锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。如果没有竞争,轻量级锁使用CAS操作避免了使用互斥量的开销,但如果存在锁竞争,除了互斥量的开销,还额外发生了CAS操作,因此在有竞争的情况下,轻量级锁会比传统的重量级锁更慢。
- 点赞
- 收藏
- 关注作者
评论(0)