“锁”的竞争必然会导致程序的整体性能下降。以下建议是为了将这种副作用降到最低。
-
减小锁持有时间
只有在必要的时候进行同步,尽可能地减少对某个锁的占有时间,以减少线程间互斥的可能,从而提高提升系统的并发能力,提高系统的吞吐量。
比较:
public synchronized void syncMethod() {
otherCode1();
mutexMethod();
otherCode2();
}
一种较为优化的解决方案如下:
public void syncMethod() {
otherCode1();
synchronized(this){
mutexMethod();
}
otherCode2();
}
-
减小锁粒度
这也是一种削弱多线程锁竞争的有效手段。所谓减小锁粒度,就是指缩小锁定对象的范围,从而减少锁冲突的可能性。典型的使用场景是ConcurrentHashMap
的实现。 -
读写分离锁来替换独占锁
在读多写少的场合,使用读写锁可以有效提升系统的并发能力。 -
锁分离
一个典型案例是LinkedBlockingQueue
。take()
和put()
分别实现了从队列中取得数据和往队列中增加数据的功能。虽然都是对当前队列做修改,但是由于其实现是基于链表的,因此,两个操作分别作用于队列的前端和尾端,理论上,两者并不冲突。固使用分离锁,而非独占锁。
/** Main lock guarding all access */
final ReentrantLock lock = new ReentrantLock();
/** Condition for waiting takes */
private final Condition notEmpty = lock.newCondition();
/** Condition for waiting puts */
private final Condition notFull = lock.newCondition();
-
锁粗化
虚拟机在遇到一连串对同一锁不断地请求和释放的操作是,便会把所有的锁操作整个成对锁的一次请求,从而减少对锁的请求同步次数,这种操作叫做锁的粗化。因为,凡事有个度,如果对同一个锁不停地请求、同步和释放,其本身也会小号系统宝贵的资源,反而不利于性能的优化。
for(int i=0;i<CIRCLE;i++){
synchronized (lock) {
//do something
}
}
与之相比,下面这种做法更加合理。
synchronized (lock) {
for(int i=0;i<CIRCLE;i++){
//do something
}
}
引用
《JAVA高并发程序设计》- 第四章
网友评论