乐观锁适合这样的场景:读不会冲突,写会冲突。同时读的频率远大于写。java
如下面的代码为例,悲观锁的实现:数据库
Java代码 网络
public Object get(Object key) { synchronized(map) { if(map.get(key) == null) { // set some values } return map.get(key); } }
乐观锁的实现:session
Java代码 架构
public Object get(Object key) { Object val = null; if((val = map.get(key) == null) { // 当map取值为null时再加锁判断 synchronized(map) { if(val = map.get(key) == null) { // set some value to map... } } } return map.get(key); }
乐观锁不能很好解决大量写冲突问题,可是若是不少场景下,锁实际上只是针对某个用户或者某个订单。好比一个用户必须先建立session,才能进行后面的操做。可是因为网络缘由,建立用户session的请求和后续请求几乎同时达到,而并行线程可能会先处理后续请求。通常状况,须要对用户sessionMap加锁,好比上面的乐观锁。在这种场景下,能够讲锁限定到用户自己上,即从原来的并发
lock.lock(); int num=storage.get(key); storage.set(key,num+1); lock.unlock();
更改成:高并发
lock.lock(key); int num=storage.get(key); storage.set(key,num+1); lock.unlock(key);
这个比较相似于数据库表锁和行锁的概念,显然行锁的并发能力比表锁高不少。ui
使用String.inter()是这种思路的一种具体实现。类 String 维护一个字符串池。 当调用 intern 方法时,若是池已经包含一个等于此 String 对象的字符串(该对象由 equals(Object) 方法肯定),则返回池中的字符串。可见,当String相同时,String.intern()老是返回同一个对象,所以就实现了对同一用户加锁。因为锁的粒度局限于具体用户,使系统得到了最大程度的并发。this
Java代码 spa
public void doSomeThing(String uid) { synchronized(uid.intern()) { // ... } }
CopyOnWriteMap?
既然说到了“相似于数据库中的行锁的概念”,就不得不提一下MVCC,Java中CopyOnWrite类实现了MVCC。Copy On Write是这样一种机制。当咱们读取共享数据的时候,直接读取,不须要同步。当咱们修改数据的时候,咱们就把当前数据Copy一份副本,而后在这个副本 上进行修改,完成以后,再用修改后的副本,替换掉原来的数据。这种方法就叫作Copy On Write。
可是,,,JDK并无提供CopyOnWriteMap,为何?下面有个很好的回答,那就是已经有了ConcurrentHashMap,为何还须要CopyOnWriteMap?
Fredrik Bromee 写道
I guess this depends on your use case, but why would you need a CopyOnWriteMap when you already have a ConcurrentHashMap?
For a plain lookup table with many readers and only one or few updates it is a good fit.
Compared to a copy on write collection:
Read concurrency:
Equal to a copy on write collection. Several readers can retrieve elements from the map concurrently in a lock-free fashion.
Write concurrency:
Better concurrency than the copy on write collections that basically serialize updates (one update at a time). Using a concurrent hash map you have a good chance of doing several updates concurrently. If your hash keys are evenly distributed.
If you do want to have the effect of a copy on write map, you can always initialize a ConcurrentHashMap with a concurrency level of 1.
String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,若是用户数特别多,致使放入字符串池的String不可控,有可能致使OOM错误或者过多的Full GC。怎么样能控制锁的个数,同时减少粒度锁呢?直接使用Java ConcurrentHashMap?或者你想加入本身更精细的控制?那么能够借鉴ConcurrentHashMap的方式,将须要加锁的对象分为多个bucket,每一个bucket加一个锁,伪代码以下:
Java代码
Map locks = new Map(); List lockKeys = new List(); for(int number : 1 - 10000) { Object lockKey = new Object(); lockKeys.add(lockKey); locks.put(lockKey, new Object()); } public void doSomeThing(String uid) { Object lockKey = lockKeys.get(uid.hash() % lockKeys.size()); Object lock = locks.get(lockKey); synchronized(lock) { // do something } }