synchronized锁优化

synchronized锁的优化

基于对象头的锁状态对synchronized锁进行优化

基于锁的状态

从低到高依次为无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态【锁只能升级不能降级】

无锁状态

偏向锁

为什么要有偏向锁?

大多数的情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得锁,为了让线程获得锁的代价更低就引入了偏向锁。

偏向锁:从始至终只有一个线程请求某一把锁。同一个对象多次加锁(重入)

获取偏向锁

当一个线程访问同步代码块获取锁时,会在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步代码块的时候不需要进行CAS操作来进行加锁和解锁,只需要测试一下对象头的Mark Word 里是否存储着指向当前线程的偏向锁。

如果成功,表示线程已经获得了锁;

如果失败,需要测试MarkWord中偏向锁的标识是否设置成1(表示当前是偏向锁),如果没有设置,则使用CAS操作竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

撤销偏向锁

偏向锁使用了一种等到竞争出现才释放锁的机制,所以只有当其他线程尝试竞争偏向锁的时候,持有偏向锁的线程才会释放偏向锁。

关闭偏向锁

在JDK6之后是默认启动的。

如果确定应用程序里所有的锁通常情况下处于竞争状态,可以通过JVM参数关闭偏向锁:-XX:-UseBiasedLocking=false,程序默认就会进入轻量级锁状态。

轻量级锁

多个线程在不同的时间段请求同一把锁,没有锁竞争,针对这种情况JVM采用了轻量级锁,来避免线程的阻塞以及唤醒。

原理:基于CAS实现,同一个时间点,经常只有一个线程竞争锁

重量级锁

悲观锁的情况

基于系统mutex锁,同一个时间点,经常有多个线程竞争

特点:mutex是系统级别加锁,线程会友用户态切换到内核态,切换成本较高(一个线程总是竞争失败,就会不停的在用户态和内核态切换,比较耗费资源;如果有很多个竞争失败的线程,性能就会有很大的影响)

锁粗化

将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁扩展成为一个范围更大的锁。即多个synchronized连续执行加锁、释放锁可以合并为一个。

示例:StringBuffer静态变量在一个线程多次append操作

静态变量属于方法区,JDK1.8之后在堆里面,是线程共享的,存在线程安全问题

public class Test{
    private static StringBuffer sb = new StringBuffer();
    public static void main(String[] args){
        sb.append("a");
        sb.append("b");
        sb.append("c");
    }
}

每次调用append方法都需要进行加锁和解锁。

JVM检测到有一系列连续的对同一个对象加锁和解锁的操作,就会及那个其合并成一次加锁和解锁的过程。

在第一次append方法时进行加锁,最后一次append方法结束后进行解锁。

锁消除

删除不必要的加锁操作。不会逃逸到其他线程的变量,可以认为是线程安全的,执行加锁操作,可以删除加锁。  

示例:StringBuffer局部变量,在一个线程多次append操作

局部变量属于虚拟机栈,是线程私有的

public class Test{
    public static void main(String[] args){
        StringBuffer sb = new StringBuffer();
        sb.append("a").append("b").append("c");
    }
}

StringBuffer的append方法是一个同步方法,但是在上面的代码中StringBuffer属于局部变量,所以该过程是安全的,完全可以不用加锁,所以可以将锁消除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值