【Java并发编程】synchronized(七):优化方案(锁消除、锁粗化)

synchronized 最大的优化莫过于在 JDK6 时引入了 偏向锁”和“轻量级锁”,从而锁级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,大幅提升了性能,

具体过程可以参考

本篇文章我们就来看看我们自己在编写代码时,有什么优化锁性能的方案

1.锁消除

锁消除即删除不必要的加锁操作。虚拟机即时编辑器在运行时,对一些“代码上要求同步,但是被检测到不可能存在共享数据竞争”的锁进行消除

根据代码逃逸技术,如果判断到一段代码中,堆上的数据不会逃逸出当前线程,那么可以认为这段代码是线程安全的,不必要加锁。

看下面这段程序:

public class SynchronizedTest {

    public static void main(String[] args) {
        SynchronizedTest test = new SynchronizedTest();

        for (int i = 0; i < 100000000; i++) {
            test.append("abc", "def");
        }
    }

    public void append(String str1, String str2) {
   
        StringBuffer sb = new StringBuffer();
        sb.append(str1).append(str2);
    }
}

在这里插入图片描述

虽然 StringBuffer 的 append 是一个同步方法,但是这段程序中的 StringBuffer 属于一个局部变量,并且不会从该方法中逃逸出去(即StringBuffer sb的引用没有传递到该方法外,不可能被其他线程拿到该引用),所以其实这过程是线程安全的,可以将锁消除。

2.锁粗化

如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作是出现在循环体中的,那即使没有出现线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。

如果虚拟机检测到有一串零碎的操作都是对同一对象的加锁,将会把加锁同步的范围扩展(粗化)到整个操作序列的外部。

举个例子:

public class StringBufferTest {

    StringBuffer stringBuffer = new StringBuffer();

    public void append(){
        stringBuffer.append("a");
        stringBuffer.append("b");
        stringBuffer.append("c");
    }
}

这里每次调用 stringBuffer.append 方法都需要加锁和解锁,如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append方法时进行加锁,最后一次append方法结束后进行解锁。

猜你喜欢

转载自blog.csdn.net/qq_33762302/article/details/114297817