锁优化
锁膨胀(无锁 —> 偏向锁 —> 轻量级锁 —> 重量级锁)
偏向锁:(只有一个线程申请锁)
- 减少线程获取锁的代价。在大多数情况下,锁不存在多线程竞争,总是由同一线程多次获取,那么此时就是偏向锁。
- 如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word的结构也就变为偏向锁的结构。
- 当该线程再次请求锁时,无需再做任何同步操作,即获取锁的过程只需要检查Mark Word的锁标记位为偏向锁以及当前线程ID等于Mark Word 的锁标记位为偏向锁以及当前线程ID等于Mark Word的ThreadID即可,这样就省去了大量有关锁申请的操作。
轻量级锁:
- 轻量级锁是由偏向锁升级而来,当存在第二个线程申请同一个锁对象时,偏向锁就会立即升级为轻量级锁。注意这里的第二个线程只是申请锁,不存在两个线程同时竞争锁,可以一前一后的交替执行同步块。
重量级锁:
- 重量级锁是由轻量级锁升级而来,当同一时间有多个线程竞争锁时,所就会被升级成重量级锁,此时其申请锁带来的开销也就会变大。
- 使用场景:该锁一般使用场景会在追求吞吐量,同步块或同步方法执行时间较长的场景。
锁消除:
- 锁消除是虚拟机一种锁的优化,这种优化更加彻底,在JIT编译时,对运行上下文进行扫描,去除不可能存在竞争的锁。
public void hello1(){
Object obj = new Object();
synchronized (obj){
System.out.println("hello word!");
}
}
public void hello2(){
Object obj = new Object();
System.out.println("hello word!");
}
锁粗化:
- 锁粗化是虚拟机对另一种极端情况的优化处理,通过扩大锁的范围,避免反复加锁和释放锁。
public void hello1() {
Object obj = new Object();
for (int i = 0; i < 1000; i++) {
synchronized (obj) {
System.out.println("hello word!");
}
}
}
public void hello2() {
Object obj = new Object();
synchronized (obj) {
for (int i = 0; i < 1000; i++) {
System.out.println("hello word!");
}
}
}
自旋锁:
- 轻量级锁失败后,虚拟机为了避免线程真实的操作系统层面挂起,还会进行一项称为自旋锁的优化手段。自旋锁在许多情况下,共享数据的锁定状态持续时间较短,切换线程不值得,通过让线程执行循环等待锁的释放,不让出CPU。如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作层面挂起,这就是自旋锁的优化方式。(有限制自选次数的设置)
- 为了限制过多占用CPU资源,一般自旋锁实现会有一个参数限定最多尝试次数超出后,自旋锁放弃当前time slice。自旋锁比较适用于锁使用者持有锁较短的情况。但它也存在缺点,如果锁被其他线程长时间占用,一直不释放CPU,会带来严重的性能开销。
自适应自旋锁:
- 对上面自旋锁优化方式进一步优化,它的自旋的次数不再固定其自旋的次数由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定,这就解决了自旋锁带来的缺点。