面试系列 | 简单谈谈你对多线程并发锁使用时的一些优化经验?

首先加锁会带来性能上的损坏,但是加锁本身不会带来多少性能消耗,性能消耗主要是在获取锁的过程。如果只有一个线程竞争锁,此时并不存在多线程竞争的情况,那么 JVM 会进行优化,这时加锁带来的性能消耗基本可以忽略。因此,优化锁的使用可以避免不必要的线程竞争,不仅可以提高程序性能,也能避免不规范加锁可能造成线程死锁问题,提高程序健壮性。

实践中常见的优化策略如下:

尽量不要锁住方法

在普通成员函数上加锁时,线程获得的是该方法所在对象的对象锁,此时整个对象都会被锁住。这也意味着,如果这个对象提供的多个同步方法是针对不同业务的,那么由于整个对象被锁住,一个业务业务在处理时其他不相关的业务线程也必须要 wait。

缩小同步代码块(只锁数据)

有时候为了编程方便,有些人会 synchnoized 很大的一块代码,如果这个代码块中的某些操作与共享资源并不相关,那么应当把它们放到同步块外部,避免长时间持有锁而造成其他线程一直处于等待状态。尤其是一些循环操作、同步 I/O 操作。不止是在代码的行数范围上缩小同步块,在执行逻辑上,也应该缩小同步块,例如多加一些条件判断,符合条件的再进行同步,而不是同步之后再进行条件判断,尽量减少不必要的进入同步块的逻辑。

锁中尽量不要再包含锁

这种情况经常发生,线程在得到了 A 锁之后,在同步方法块中调用了另外对象的同步方法,获得了第二个锁,这样可能导致一个调用堆栈中有多把锁的请求,多线程情况下可能会出现很复杂、难以分析的异常情况,导致死锁的发生。具体案例如下:

//错误示范1
synchronized(A){
   synchronized(B){
       //TODO
   }  
}
//错误示范2
synchronized(A){
    B  b = list.get(0);
    b.func(); //这是一个B的同步方法
}
//正确示范1
{
    B b = null;
    synchronized(A){
        b = list.get(0);
    }
    b.method(); //跳出来加锁,不包含加锁
}
将锁私有化(在内部管理锁)

把锁作为一个私有的对象,外部不能拿到这个对象,更安全一些。对象可能被其他线程直接进行加锁操作,此时线程便持有了该对象的对象锁,所以推荐的做法是把 private Object lock = new Object(); 对象作为私有的锁对象。

进行适当的锁分解

其实也就是锁范围最小化一个原则,有些场景我们的业务很复杂,为了方便大块锁在了一起,其实如果仔细分析是可以拆分成多段的,与其一大段被锁着只能一个线程顺序处理,还不如分成多个小段来提升一些效率。

选择合适特性的锁

譬如有些场景适合 synchnoized,有些适合 Lock 家族不同的锁,譬如可重入锁,互斥锁,共享锁,读写锁等,我们要依据自己业务场景选择适合自己特性的锁。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值