锁偏向
如果一个线程获得了锁,那么锁就进入了偏向模式。在进行锁偏向优化时会借用锁对象的Mark Word区域(对象头的第一部分,用来存储对象的运行时数据,Hash码,GC分代年龄等),Mark Word 里用两个bit用来存储锁状态,一个bit表示锁是否是偏向模式(默认为0,无偏向)。
当锁对象第一次被线程获取的时候,JVM将Mark Word 中的标志位设为“01”,偏向位设为“1”,用CAS操作将获取到锁的线程的ID记录到锁对象的Mark Word 中。当另一个线程尝试获取这个锁时,偏向锁马上结束。
当持有锁的线程再次请求锁时,不用再进行同步操作,减少了锁申请的操作。在没有锁竞争或者竞争少的情况下,有较好的性能,而在锁竞争大的情况下,可能每次获取锁的线程都不同,所以效果不好。
轻量级锁
进入偏向锁失败后,JVM不会立即挂起线程,会进入轻量级锁优化。
JVM在当前线程的栈帧中建立一块锁记录(Lock Record)空间,存储锁对象的Mark Word 的拷贝,然后使用CAS把对象的Mark Word 更新为指向Lock Word 的指针,表示当前线程拥有这个对象的锁,Mark Word 的锁标志位置为“00”。
如果更新失败,说明至少有一条线程与当前线程竞争该锁。JVM会先检查对象的Mark Word 是否指向当前线程的栈帧,如果是说明当前线程持有了锁,直接进入临界区执行同步块即可,否则说明该锁被其他线程持有了。如果有两条以上的线程争用一个锁,那么轻量级锁会膨胀为重量级锁。
轻量级锁释放的过程同样是用了CAS算法,如果锁对象的Mark Word 依然指向当前线程,就用CAS操作把Mark Word 和 线程中的 Lock Record 替换回来,如果替换失败,说明有其他线程尝试过获取该锁,在释放锁时还要唤醒被挂起的线程。
轻量级锁的性能提升依据是:对于大部分锁,同步周期内不存在竞争。没有竞争,通过CAS操作能避免使用互斥量,减少开销。如果竞争多,CAS和使用互斥量的开销比重量级锁大。
自旋锁和自适应自旋锁
线程获取锁失败后,系统假设在不久后线程能获取到锁,JVM会让线程执行几次空循环后再尝试获取锁,如果依然不能获取锁,才让操作系统将线程挂起。
自适应自旋意思是自旋的时间不是固定的。普通的自旋对于JVM里的所有锁自旋的时间都是一样的,是固定的。而自适应自旋使自选的时间可以根据上一次在同一个锁的自旋时间和锁的拥有者状态来决定。自旋时,如果在同一个锁对象上刚刚成功获得过锁,且持有锁的线程正在运行,JVM认为这次自旋很可能也成功,允许更长的自旋时间。而如果在某个锁上的自旋很少成功,以后在该锁上的自旋时间会很短甚至直接跳过。
锁消除
JVM在JIT编译时,去除不存在共享资源竞争的锁,比如在没有竞争的情况下使用Vector(内部通过sycnchronized保证线程安全),会被锁消除优化。
锁粗化
当一段代码里连续对同一个对象进行上锁和解锁时,JVM会将这一系列锁操作粗化为整个操作序列的一个锁,这样只需上锁一次,相对于多次上锁和解锁性能消耗小很多。
重量级锁
当有一个线程获取锁后,其他线程尝试获取锁时都会进入阻塞状态。