【Java并发编程】锁机制(三):synchronized优化方案&使用注意

1.synchronized优化方案

1.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的引用没有传递到该方法外,不可能被其他线程拿到该引用),所以其实这过程是线程安全的,可以将锁消除。

1.2 锁粗化

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

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

举个例子:

public class StringBufferTest {
    
    
    StringBuffer stringBuffer = new StringBuffer();

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

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

2.synchronized使用注意事项

  • sync加在静态方法(static)时锁的是类,比如 sync(A.class)

  • sync的锁粒度应该尽量小,保证原子性即可

  • synchronized遇到异常时会自动释放锁,需要在catch块中做处理

    扫描二维码关注公众号,回复: 11712055 查看本文章
  • sync只能锁住堆,不要锁String(方法区-常量池)

  • sync是可重入锁,在sync块中调用sync方法自动获得锁

  • 模拟死锁

猜你喜欢

转载自blog.csdn.net/weixin_43935927/article/details/108656476