想用ConcurrentHashMap做一个简单的缓存,存储对象,假如有如下场景:
A a = map.get(key);
if(a==null)
{
a = new A();
map.putIfAbsent(key,a);
}
else{
a.setTimes(i++);
map.put(key,a);
}
这样是否能保证对象a的判空,修改,以及map读写是线程安全的
如果A的构造不耗时,是否有必要处理为线程安全的写法
------解决方案--------------------
不能。
比如你判断了a!=null;正准备执行else的语句。很不巧另外一个线程对你的map进行的操作,导致你的a现在等于null
,之前的线程继续执行else语句,这个时候a=null就会抛出空指针异常。
一般比较常用的方法是用synchronized关键字
------解决方案--------------------
这肯定不行了,虽然ConcurrentHashMap是线程安全的,但是像你这样写,就不是了,你应该用synchronized块,把那段判断语句包围起来!
------解决方案--------------------
private final static ConcurrentMap<String, Future<Object>> cached = new ConcurrentHashMap<String, Future<Object>>();
public Object get(String key) {
Future<Object> future = cached.get(key);
if (future == null) {
Future<Object> newFutrue = new FutureTask<Object>(
new Callable<Object>() {
@Override
public Object call() throws Exception {
Object object = null;
//TODO 真正的获取value
return object;
}
});
// put if absent 防止缓存穿透。防止同一个Key,多个线程没命中的情况下,穿透至缓存。
future = cached.putIfAbsent(key, newFutrue);
if (future == null) {
future = newFutrue;
}
((FutureTask<Object>) future).run();
}
Object value = null;
try {
value = future.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return value;
}
本地缓存,请用Future 解决两个问题,
1 不用同步 2 解决缓存穿透,防止同一个KEY 没命中之后,同时访问后端。
------解决方案--------------------
正确的用法是:
V v = map.get(k);
if (v == null) {
V _v = new V();
v = map.putIfAbsent(k, _v);
if (v == null) {v = _v;}
}
这段代码可以在线程中执行,得到正确的 v。