查看一个新来同事的代码(比我高两个级别,心态略崩),发现一个典型的多线程并发问题,在此记录一下。
需求:有个数据需要从其他服务获取,采用rpc的方式获取到后在本地缓存10秒钟 经简化形成后如下代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Scratch {
public static final long REPLACE_URL_TIME_OUT = 10000l;
public static final long REPLACE_URL_TIME_OUT_E = 9000l;
private static String replaceLink;
private static long replaceLinkTime = 0l;
private static volatile boolean isQuery = false;
private String getUsableUrl() {
Long nowTime = System.currentTimeMillis();
boolean isTimeOut = nowTime - replaceLinkTime > REPLACE_URL_TIME_OUT;
if (replaceLink!=null && !isTimeOut) {
return replaceLink;
}
if (isQuery) {
return replaceLink;
}
try {
isQuery = true;
String usableUrl = "从duubo获取到的";
if (usableUrl==null) {
System.out.println("获取失败");
replaceLinkTime = nowTime - REPLACE_URL_TIME_OUT_E;
return replaceLink;
}
System.out.println("当前时间:{" + nowTime + "},上次访问时间:{" + replaceLinkTime + "},是否超时:{}" + isTimeOut);
replaceLink = usableUrl;
replaceLinkTime = nowTime;
} catch (Exception e) {
replaceLinkTime = nowTime - REPLACE_URL_TIME_OUT_E;
System.out.println("获取异常" );
} finally {
isQuery = false;
}
return replaceLink;
}
public static void main(String[] args) {
Scratch aspect = new Scratch();
ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 20; i++) {
threadPool.execute(() -> System.out.println(Thread.currentThread().getName()+aspect.getUsableUrl()));
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 20; i++) {
threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + aspect.getUsableUrl()));
}
}
}
代码分析:
- 高并发下问题一
首先replaceLink为null多个线程访问时,其中有一个线程将isQuery设置为true,其他线程判断isQuery这个字段时已经为true而此时replaceLink还是为null,该方法将直接返回null。
if (isQuery) {
return replaceLink;
}
try {
isQuery = true;
。。。。。。
- 高并发下问题二
已经过了过期时间。多个线程走到如下代码处都判断isQuery为false从而都去调用dubbo接口获取最新数据。造成缓存击穿问题(大量请求不走缓存)
if (isQuery) {
return replaceLink;
}
-
Volatitle关键字理解不足 Volatitle关键字的作用是保证内存可见性和禁止指令重排序。深入理解虚拟机第三版12.3.3 606页给出了其中的使用场景。
|