返回信息流import java.util.HashMap;
import java.util.Map;
public class ThreadSafeCache {
private Map<String,Prod> prodMap = new HashMap<String,Prod>();
private long exireMills = 1000;
public Prod getProd(String prodNo) {
Prod prod = prodMap.get(prodNo);
if(prod == null) {
synchronized (this) {
prod = prodMap.get(prodNo);
if(prod == null) {
prod = loadProd(prodNo);
prodMap.put(prodNo, prod);
return prod;
} else {
return prod;
}
}
} else {
if(prod.ts + exireMills > System.currentTimeMillis()) {
synchronized (this) {
prod = prodMap.get(prodNo);
if(prod.ts + exireMills > System.currentTimeMillis()) {
prod = loadProd(prodNo);
prodMap.put("prodNo", prod);
return prod;
} else {
return prod;
}
}
} else {
return prod;
}
}
}
private Prod loadProd(String prodNo) {
Prod prod = new Prod();
prod.prodName = "aaa";
prod.prodNo = prodNo;
prod.prodPrice = 1.0;
prod.ts = System.currentTimeMillis();
return prod;
}
}
class Prod {
public String prodNo;
public String prodName;
public Double prodPrice;
public long ts;
}
这是一条镜像帖。来源:北邮人论坛 / java / #41728同步于 2015/6/17
该镜像源已超过 30 天没有更新,可能在源站已被删除。
Java机器人发帖
大神们帮忙看看这个缓存是不是线程安全的
nxlhero
2015/6/17镜像同步36 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
不是。
如果有一个线程执行到getProd里面的prodMap.put的时候,碰巧另一个线程调用getProd,然后里面第一个语句调用prodMap.get方法,他们两个就冲突了。
不太明白为什么用prod = prodMap.get("prodNo"); 不应该是prod = prodMap.get(prodNo);么?
气氛上说因为双重加锁检查会遇到指令重排的问题,另一个线程有可能在prod = prodMap.get("prodNo");的时候拿到一个没有初始化结束的对象。
如果想要线程安全, 用ConcurrentHashMap就行了吧,好像有个叫putIfAbsent的函数。而且既然是cache,也不在意多create的事情吧。。(就是比如两个线程同时发现没有,去create,然后putIfAbsent,应该只有一个成功,另一个就扔掉了)
【 在 nxlhero 的大作中提到: 】
: [code=java]
: import java.util.HashMap;
: import java.util.Map;
: ...................
啊对,暖神说的对,get方法和put方法会冲突。。。即使双重加锁检查了。
【 在 aiquestion 的大作中提到: 】
: 不太明白为什么用prod = prodMap.get("prodNo"); 不应该是prod = prodMap.get(prodNo);么?
: 气氛上说因为双重加锁检查会遇到指令重排的问题,另一个线程有可能在prod = prodMap.get("prodNo");的时候拿到一个没有初始化结束的对象。
: 如果想要线程安全, 用ConcurrentHashMap就行了吧,好像有个叫putIfAbsent的函数。而且既然是cache,也不在意多create的事情吧。。(就是比如两个线程同时发现没有,去create,然后putIfAbsent,应该只有一个成功,另一个就扔掉了)
: ...................
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html
嗯。ConcurrentHashMap很好很强大。Java8还增加了computeIfAbsent函数,正是你想要的。
【 在 nuanyangyang 的大作中提到: 】
: 不是。
: 如果有一个线程执行到getProd里面的prodMap.put的时候,碰巧另一个线程调用getProd,然后里面第一个语句调用prodMap.get方法,他们两个就冲突了。
表示多线程真心不是很懂。。。
你看,线程A执行到prodMap.put的时候就算另外一个线程B调用了prodMap.get也就拿到null呗,然后不就进入同步区域了嘛。为啥会出问题?
【 在 Monologue 的大作中提到: 】
:
: 表示多线程真心不是很懂。。。
: 你看,线程A执行到prodMap.put的时候就算另外一个线程B调用了prodMap.get也就拿到null呗,然后不就进入同步区域了嘛。为啥会出问题?
没这么简单。也许put操作不是原子的,put的过程中会把数据结构弄成某种非法的结构,然后就会出现各种难以预料的错误,比如读到本不该读到的值,或者发生null指针异常,或者干脆HashMap发现有并发的结构修改(put之类会改变数据结构的操作)而直接抛出ConcurrentModificationException。
至于“然后”进入synchronized区域,已经晚了,异常可能已经发生了。
[ema11]
晓得了,原来是因为put不是原子操作。思维有点僵化啊,考虑的不够
【 在 nuanyangyang 的大作中提到: 】
:
: 没这么简单。也许put操作不是原子的,put的过程中会把数据结构弄成某种非法的结构,然后就会出现各种难以预料的错误,比如读到本不该读到的值,或者发生null指针异常,或者干脆HashMap发现有并发的结构修改(put之类会改变数据结构的操作)而直接抛出ConcurrentModificationException。
: 至于“然后”进入synchronized区域,已经晚了,异常可能已经发生了。
: ...................
put不是原子的,比如: 如果大小不够,会resize的。and concurrentHashMap这个不错,或者你可以自己套一层wrapper给HashMap,达到同步的目的。。