Java多线程编程概述
Java多线程的安全问题
Java多线程同步
Java多线程间的通信
Java线程Lock
Java多线程管理
保障线程安全的设计技术
Java锁的优化及注意事项
Java多线程集合
【Java多线程】单例模式与多线程

JVM锁优化

锁偏向

锁偏向是一种针对加锁操作的优化,如果一个线程获得了锁,那么锁就进入偏向模式, 当这个线程再次请求锁时,无须再做任何同步操作,这样可以节省有关锁申请的时间,提高了程序的性能。

锁偏向在没有锁竞争的场合可以有较好的优化效果,对于锁竞争 比较激烈的场景,效果不佳, 锁竞争激烈的情况下可能是每次都是不同的线程来请求锁,这时偏向模式失效。

量级锁

如果锁偏向失败,JVM不会立即挂起线程,还会使用一种称为轻量级锁的优化手段. 会将对象的头部作为指针,指向持有锁的线程堆栈内部, 来判断一个线程是否持有对象锁. 如果线程获得轻量级锁成功,就进入临界区. 如果获得轻量级锁失败,表示其他线程抢到了锁,那么当前线程的锁的请求就膨胀为重量级锁.当前线程就转到阻塞队列中变为阻塞状态。

偏向锁,轻量级锁都是乐观锁,重量级锁是悲观锁。

一个对象刚开始实例化时,没有任何线程访问它,它是可偏向的,即它认为只可能有一个线程来访问它,所以当第一个线程来访问它的时候,它会偏向这个线程. 偏向第一个线程,这个线程在修改对象头成为偏向锁时使用CAS操作,将对象头中ThreadId改成自己的ID,之后再访问这个对象时,只需要对比ID即可. 一旦有第二个线程访问该对象,因为偏向锁不会主动释放,所以第二个线程可以查看对象的偏向状态,当第二个线程访问对象时,表示在这个对象上已经存在竞争了,检查原来持有对象锁的线程是否存活,如果挂了则将对象变为无锁状态,然后重新偏向新的线程; 如果原来的线程依然存活,则马上执行原来线程的栈,检查该对象的使用情况,如果仍然需要偏向锁,则偏向锁升级为轻量级锁。

轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对同一个锁的操作会错开,或者稍微等待一下(自旋)另外一个线程就会释放锁. 当自旋超过一定次数,或者一个线程持有锁,一个线程在自旋,又来第三个线程访问时, 轻量级锁会膨胀为重量级锁, 重量级锁除了持有锁的线程外,其他的线程都阻塞。

自旋锁

锁膨胀后,JVM为了避免线程在真实的层面被挂起,JVM还会做最后的努力,这就是自旋锁. 当前线程无法立即获得锁,但是在什么时候可以获得锁也不一定, 也许在几个CPU周期后就可以得到锁, 如果是这样的话,简单的将线程挂起可能是一种得不偿失的操作. 因此JVM会进行一次赌注: JVM期望在不久的将来可以得到锁. 因为JVM会让当前的线程做几个空循环,在经过若干次循环后,如果可以得到锁就进入临界区,如果还不能得到锁则将线程真实的挂起。

锁消除

锁消除是一种更彻底的锁优化, JVM在JIT编译时,会通过扫描上下文,去除不可能存在共享资源竞争的锁, 通过锁消除,可以节省毫无意义的请求锁时间。