浅析Java锁优化

减少锁的持有时间

在程序设计的时候,我们应该尽量减少锁的持有时间来减少线程间互斥的可能,锁的持有的时间越长,锁竞争就越激烈。
例如一个同步方法里面,方法实现分为三个步骤,其中步骤一和步骤三不需要同步,而且是耗时比较长的方法,而步骤二是需要同步,如果我们对整个方法进行同步处理,那么在并发量比较大的时候,当一个线程进入方法获得锁之后,只有方法完成之后才会释放锁,由于步骤一和三耗时比较长,导致在锁上的等待线程大量增加,竞争激烈,系统性能下降的。因此我们应该减少锁的持有时间。

1
2
3
4
5
public synchronized void syncMethod(){
spendLongTimeMethod();
mutexMethod();
notSyncMethod();
}

我们应该在需要同步的时候的才进行同步,减少锁的持有时间,提高系统的吞吐量。
我们只针对metexMethod()进行了同步,其他步骤不需要同步,降低了锁冲突的可能,提高了系统的并发能力。

1
2
3
4
5
6
7
public  void syncMethod(){
notSyncMethod();
synchronized (this) {
mutexMethod();
}
spendLongTimeMethod();
}

减少锁的粗粒度

减少锁的粗粒度,其实就是减少锁定对象的范围,减少锁冲突的可能性。
减少锁的粗粒度在Java1.7的ConcurrentHashMap的锁分段机制的实现,ConcurrentHashMap将整个map的Entry数组分为16个段ssegment(默认为16),在对ConcurrentHashMap进行put操作的时候,不是对整个entry数组加锁,而是根据key的hashcode值判断数据应该放在哪个segment中,然后对该segment加锁,执行put操作,只要要添加的数据没有放在同一个段中,就可以实现并行。
但是减少锁的粗粒度会导致获取全局锁的时候,需要获取所有子锁的锁才能完成,性能比普通的hashmap差。

锁分离

锁分离是指根据程序的功能特点,将一个独占锁分为多个锁。
锁分离在LinkedBlockingQueue在实现,LinkedBlockingQueue中的take和put实现了队列的移除元素和添加元素的功能,它们都对当前队列进行了修改操作,但是他们作用的数据地点都不一样,一个是队列的头部,一个是队列的尾部,如果我们使用独占锁对队列进行锁定的话,那么take和put操作就不能并发执行了,因此将take和put操作的锁分离,使用了两把锁进行独立,take和put操作之间不存在竞争关系。通过使用putLock和takeLock实现读数据和写数据的分离,提高了并发性。

锁粗化

虚拟机会把一连串连续对同一锁不断进行请求和释放的操作时,会把所有的锁操作整合成对锁的一次请求,从而减少对锁的请求同步次数,这个称为锁的粗化。
在通常情况下,为了提供系统的并发性,都会要求线程持有锁的时间尽可能的短,就是在使用完共享资源之后,就应该立即释放锁,但是如果短时间内同一个锁不断请求,同步和释放操作的话,会消耗系统的资源。
因此在某些适合的场合进行锁的粗化,例如在循环请求锁的时候。

COMMENT AND SHARE

冼毅俊

Xupter√Java√德桌迷√虐心控√mugen爱好者√音乐杂食党√小说发烧友√基本色√鱼迷√stan√bitch√daydreamer


Java研发


长安