java高并发锁的3种实现示例代码
初级技巧-乐观锁
乐观锁适合这样的场景:读不会冲突,写会冲突。同时读的频率远大于写。
以下面的代码为例,悲观锁的实现:
publicObjectget(Objectkey){
synchronized(map){
if(map.get(key)==null){
//setsomevalues
}
returnmap.get(key);
}
}
乐观锁的实现:
publicObjectget(Objectkey){
Objectval=null;
if((val=map.get(key)==null){
//当map取值为null时再加锁判断
synchronized(map){
if(val=map.get(key)==null){
//setsomevaluetomap...
}
}
}
returnmap.get(key);
}
中级技巧-String.intern()
乐观锁不能很好解决大量写冲突问题,但是如果很多场景下,锁实际上只是针对某个用户或者某个订单。比如一个用户必须先创建session,才能进行后面的操作。但是由于网络原因,创建用户session的请求和后续请求几乎同时达到,而并行线程可能会先处理后续请求。一般情况,需要对用户sessionMap加锁,比如上面的乐观锁。在这种场景下,可以讲锁限定到用户本身上,即从原来的
lock.lock(); intnum=storage.get(key); storage.set(key,num+1); lock.unlock();
更改为:
lock.lock(key); intnum=storage.get(key); storage.set(key,num+1); lock.unlock(key);
这个比较类似于数据库表锁和行锁的概念,显然行锁的并发能力比表锁高很多。
使用String.inter()是这种思路的一种具体实现。类String维护一个字符串池。当调用intern方法时,如果池已经包含一个等于此String对象的字符串(该对象由equals(Object)方法确定),则返回池中的字符串。可见,当String相同时,String.intern()总是返回同一个对象,因此就实现了对同一用户加锁。由于锁的粒度局限于具体用户,使系统获得了最大程度的并发。
publicvoiddoSomeThing(Stringuid){
synchronized(uid.intern()){
//...
}
}
CopyOnWriteMap?
既然说到了“类似于数据库中的行锁的概念”,就不得不提一下MVCC,Java中CopyOnWrite类实现了MVCC。CopyOnWrite是这样一种机制。当我们读取共享数据的时候,直接读取,不需要同步。当我们修改数据的时候,我们就把当前数据Copy一份副本,然后在这个副本上进行修改,完成之后,再用修改后的副本,替换掉原来的数据。这种方法就叫做CopyOnWrite。
但是,,,JDK并没有提供CopyOnWriteMap,为什么?下面有个很好的回答,那就是已经有了ConcurrentHashMap,为什么还需要CopyOnWriteMap?
FredrikBromee写道
Iguessthisdependsonyourusecase,butwhywouldyouneedaCopyOnWriteMapwhenyoualreadyhaveaConcurrentHashMap?
Foraplainlookuptablewithmanyreadersandonlyoneorfewupdatesitisagoodfit.
Comparedtoacopyonwritecollection:
Readconcurrency:
Equaltoacopyonwritecollection.Severalreaderscanretrieveelementsfromthemapconcurrentlyinalock-freefashion.
Writeconcurrency:
Betterconcurrencythanthecopyonwritecollectionsthatbasicallyserializeupdates(oneupdateatatime).Usingaconcurrenthashmapyouhaveagoodchanceofdoingseveralupdatesconcurrently.Ifyourhashkeysareevenlydistributed.
Ifyoudowanttohavetheeffectofacopyonwritemap,youcanalwaysinitializeaConcurrentHashMapwithaconcurrencylevelof1.
高级技巧-类ConcurrentHashMap
String.inter()的缺陷是类String维护一个字符串池是放在JVMperm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的FullGC。怎么样能控制锁的个数,同时减小粒度锁呢?直接使用JavaConcurrentHashMap?或者你想加入自己更精细的控制?那么可以借鉴ConcurrentHashMap的方式,将需要加锁的对象分为多个bucket,每个bucket加一个锁,伪代码如下:
Maplocks=newMap();
ListlockKeys=newList();
for(intnumber:1-10000){
ObjectlockKey=newObject();
lockKeys.add(lockKey);
locks.put(lockKey,newObject());
}
publicvoiddoSomeThing(Stringuid){
ObjectlockKey=lockKeys.get(uid.hash()%lockKeys.size());
Objectlock=locks.get(lockKey);
synchronized(lock){
//dosomething
}
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。