Java SE 1.6对锁进行了大量优化工作,引入轻量级锁和偏向锁,这里只对这两种优化作说明。Java SE 1.6中的锁状态分为无锁、偏向锁、轻量锁、重量锁4种,级别依次递增,性能越来越差,且升级后不允许降级,这么做能在多数环境下有利于锁的获取和释放。
我们知道Java对象的头部是记录了对象的相关信息,具体情况看我另外文章,这里放张图好对照:
上图是对象头中Mark Word信息,锁的相关信息存放在这里。另外,有几点前提需要说明下,线程在执行同步代码之前,JVM会首先在当前线程的栈帧中创建用于存储锁记录的空间,然后将对象头中的Mark Word复制到线程栈帧中,
具体操作过程是当某个线程访问同步块时,并不是直接去竞争锁,首先判断偏向锁相关信息,当然前提是JVM开启了偏向锁,可以通过-XX:-UseBiasedLocking=false来关闭偏向锁,关闭偏向锁后,JVM默认进入轻量级锁状态。在开启偏向锁的环境下,线程进入同步块时,会首先读取对象头中偏向线程ID是否和当前线程ID一致,如果一致,直接得到锁,如果不一致,需要读取对象头中偏向锁标记是否=1,如果是(是偏向锁),使用CAS设置对象头中偏向锁线程ID为当前线程,并得到锁,如果对象头中偏向锁标记没有设置,使用CAS竞争锁。偏向锁的撤销过程如下:先暂停持有偏向锁的线程,然后检查目标线程存活与否,如果死了,直接设置对象头偏向线程ID为空,和偏向锁标记,也即无锁状态,如果目标线程还存活,设置对象头使其偏向其他线程或设置对象头为无锁状态或设置对象头为不合适偏向锁状态,最后唤醒暂停的线程。
为什么要拷贝Mark word,是不想在lock和unlock这种操作上再加锁,Mark word的锁指针指向lock record是为了让其他线程知道该object monitor已经被占用,lock record的owner指向Mark word是为了在同步块的执行中,知道哪个object被锁住了。轻量级锁主要细分为自旋锁和自适应自旋锁。