Skip to content

Commit f503906

Browse files
Cache
1 parent 85af47c commit f503906

File tree

9 files changed

+404
-0
lines changed

9 files changed

+404
-0
lines changed

Cache/Cache.iml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="JAVA_MODULE" version="4">
3+
<component name="NewModuleRootManager" inherit-compiler-output="true">
4+
<exclude-output />
5+
<content url="file://$MODULE_DIR$">
6+
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
7+
</content>
8+
<orderEntry type="inheritedJdk" />
9+
<orderEntry type="sourceFolder" forTests="false" />
10+
</component>
11+
</module>

Cache/src/CacheUsage1.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import java.util.HashMap;
2+
import java.util.concurrent.TimeUnit;
3+
4+
/**
5+
* 最简单的缓存形式 -> HashMap
6+
* 性能差 + 代码复用性差(侵入性高) + 并发不安全
7+
*
8+
* @Author: zzStar
9+
* @Date: 10-13-2020 12:43
10+
*/
11+
public class CacheUsage1 {
12+
13+
private final HashMap<String, Integer> cache = new HashMap<>();
14+
15+
public synchronized Integer computer(String userId) throws InterruptedException {
16+
//先检查hashmap里面有没有保存过之前的计算结果
17+
Integer result = cache.get(userId);
18+
if (result == null) {
19+
//缓存中没有,则现在计算,计算后保存到cache里
20+
result = doCompute(userId);
21+
cache.put(userId, result);
22+
}
23+
return result;
24+
}
25+
26+
//业务代码
27+
private Integer doCompute(String userId) throws InterruptedException {
28+
TimeUnit.SECONDS.sleep(1);
29+
return Integer.valueOf(userId);
30+
}
31+
32+
public static void main(String[] args) throws InterruptedException {
33+
CacheUsage1 cacheUsage1 = new CacheUsage1();
34+
System.out.println("calculating");
35+
Integer result = cacheUsage1.computer("13");
36+
System.out.println("result = " + result);
37+
result = cacheUsage1.computer("13");
38+
System.out.println("result = " + result);
39+
}
40+
}

Cache/src/CacheUsage2.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import java.util.HashMap;
2+
3+
/**
4+
* 装饰者模式解耦
5+
*
6+
* @Author: zzStar
7+
* @Date: 10-13-2020 13:03
8+
*/
9+
public class CacheUsage2<A, V> implements Computable<A, V> {
10+
11+
private final HashMap<A, V> cache = new HashMap<>();
12+
13+
private final Computable<A, V> computable;
14+
15+
public CacheUsage2(Computable<A, V> computable) {
16+
this.computable = computable;
17+
}
18+
19+
@Override
20+
public synchronized V compute(A arg) throws Exception {
21+
System.out.println("cache");
22+
V result = cache.get(arg);
23+
if (result == null) {
24+
result = computable.compute(arg);
25+
cache.put(arg, result);
26+
}
27+
return result;
28+
}
29+
30+
public static void main(String[] args) throws Exception {
31+
CacheUsage2<String, Integer> usage2 = new CacheUsage2<>(new ExpensiveFunc());
32+
Integer result = usage2.compute("1333");
33+
System.out.println("result = " + result);
34+
result = usage2.compute("1333");
35+
System.out.println("result = " + result);
36+
37+
}
38+
}

Cache/src/CacheUsage3.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import java.util.HashMap;
2+
3+
/**
4+
* 多线程访问的性能问题:在多线程的情况下,只能一个线程进入cache 的计算方法
5+
*
6+
* @Author: zzStar
7+
* @Date: 10-13-2020 13:03
8+
*/
9+
public class CacheUsage3<A, V> implements Computable<A, V> {
10+
11+
private final HashMap<A, V> cache = new HashMap<>();
12+
13+
private final Computable<A, V> computable;
14+
15+
public CacheUsage3(Computable<A, V> computable) {
16+
this.computable = computable;
17+
}
18+
19+
/*
20+
@Override
21+
public synchronized V compute(A arg) throws Exception {
22+
System.out.println(Thread.currentThread().getName() + " 进入缓存机制");
23+
V result = cache.get(arg);
24+
if (result == null) {
25+
result = computable.compute(arg);
26+
cache.put(arg, result);
27+
}
28+
return result;
29+
}
30+
*/
31+
32+
//缩小锁的粒度,仍不意味着线程安全,还需要考虑到同时读写等情况
33+
@Override
34+
public V compute(A arg) throws Exception {
35+
System.out.println(Thread.currentThread().getName() + " 进入缓存机制");
36+
V result = cache.get(arg);
37+
if (result == null) {
38+
result = computable.compute(arg);
39+
synchronized (this) {
40+
cache.put(arg, result);
41+
}
42+
}
43+
return result;
44+
}
45+
46+
47+
public static void main(String[] args) {
48+
CacheUsage3<String, Integer> usage3 = new CacheUsage3<>(new ExpensiveFunc());
49+
new Thread(() -> {
50+
try {
51+
Integer result = usage3.compute("7878");
52+
System.out.println(Thread.currentThread().getName() + " result=" + result);
53+
} catch (Exception e) {
54+
e.printStackTrace();
55+
}
56+
}).start();
57+
new Thread(() -> {
58+
try {
59+
Integer result = usage3.compute("7878");
60+
System.out.println(Thread.currentThread().getName() + " result=" + result);
61+
} catch (Exception e) {
62+
e.printStackTrace();
63+
}
64+
}).start();
65+
new Thread(() -> {
66+
try {
67+
Integer result = usage3.compute("8989");
68+
System.out.println(Thread.currentThread().getName() + " result=" + result);
69+
} catch (Exception e) {
70+
e.printStackTrace();
71+
}
72+
}).start();
73+
}
74+
}

Cache/src/CacheUsage4.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import java.util.HashMap;
2+
import java.util.Map;
3+
import java.util.concurrent.ConcurrentHashMap;
4+
5+
/**
6+
* (使用)ConcurrentHashMap >>>> 问题:出现线程重复计算的问题
7+
*
8+
* @Author: zzStar
9+
* @Date: 10-13-2020 13:03
10+
*/
11+
public class CacheUsage4<A, V> implements Computable<A, V> {
12+
13+
private final Map<A, V> cache = new ConcurrentHashMap<>();
14+
15+
private final Computable<A, V> computable;
16+
17+
public CacheUsage4(Computable<A, V> computable) {
18+
this.computable = computable;
19+
}
20+
21+
22+
@Override
23+
public V compute(A arg) throws Exception {
24+
System.out.println(Thread.currentThread().getName() + " 进入缓存机制");
25+
V result = cache.get(arg);
26+
if (result == null) {
27+
result = computable.compute(arg);
28+
cache.put(arg, result);
29+
}
30+
return result;
31+
}
32+
33+
34+
public static void main(String[] args) {
35+
CacheUsage4<String, Integer> usage3 = new CacheUsage4<>(new ExpensiveFunc());
36+
new Thread(() -> {
37+
try {
38+
Integer result = usage3.compute("5656");
39+
System.out.println(Thread.currentThread().getName() + " result=" + result);
40+
} catch (Exception e) {
41+
e.printStackTrace();
42+
}
43+
}).start();
44+
new Thread(() -> {
45+
try {
46+
Integer result = usage3.compute("5656");
47+
System.out.println(Thread.currentThread().getName() + " result=" + result);
48+
} catch (Exception e) {
49+
e.printStackTrace();
50+
}
51+
}).start();
52+
new Thread(() -> {
53+
try {
54+
Integer result = usage3.compute("9090");
55+
System.out.println(Thread.currentThread().getName() + " result=" + result);
56+
} catch (Exception e) {
57+
e.printStackTrace();
58+
}
59+
}).start();
60+
}
61+
}

Cache/src/CacheUsage5.java

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import java.util.Map;
2+
import java.util.concurrent.*;
3+
4+
/**
5+
* @Description: 利用future避免重复计算 + 缓存过期到期自动失效
6+
* @Author: zzStar
7+
* @Date: 2020/10/13 13:45
8+
*/
9+
public class CacheUsage5<A, V> implements Computable<A, V> {
10+
11+
private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();
12+
13+
private final Computable<A, V> computable;
14+
15+
public CacheUsage5(Computable<A, V> computable) {
16+
this.computable = computable;
17+
}
18+
19+
20+
@Override
21+
public V compute(A arg) {
22+
Future<V> future = cache.get(arg);
23+
if (future == null) {
24+
Callable<V> callable = () -> computable.compute(arg);
25+
FutureTask<V> futureTask = new FutureTask<>(callable);
26+
27+
//原子组合操作,每一个线程进来都先做一次判断
28+
future = cache.putIfAbsent(arg, futureTask);
29+
if (future == null) {
30+
future = futureTask;
31+
System.out.println("从FutureTask调用计算函数");
32+
futureTask.run();
33+
}
34+
//计算之前放入缓存
35+
//cache.put(arg, futureTask);
36+
}
37+
V v = null;
38+
try {
39+
//计算的时候有可能会出现异常
40+
v = future.get();
41+
} catch (InterruptedException | ExecutionException e) {
42+
System.out.println("出现异常 " + e.getMessage());
43+
//取消线程任务,发送中断信号
44+
future.cancel(true);
45+
}
46+
//在有结果之前会阻塞
47+
return v;
48+
}
49+
50+
//延迟功能
51+
private final static ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
52+
53+
//缓存过期功能,重载
54+
public V compute(A arg, long expire) {
55+
//超时时间>0
56+
if (expire > 0) {
57+
service.schedule(() -> {
58+
//如果还在计算,则直接取消任务
59+
Future<V> vFuture = cache.get(arg);
60+
if (vFuture != null) {
61+
if (!vFuture.isDone()) {
62+
vFuture.cancel(true);
63+
}
64+
}
65+
cache.remove(arg);
66+
System.out.println("过期时间到了,清除缓存:" + arg);
67+
}, expire,TimeUnit.MILLISECONDS);
68+
}
69+
service.shutdown();
70+
return compute(arg);
71+
}
72+
73+
//把缓存的时间设置为随机,过期时间不设置为统一,避免缓存雪崩
74+
public V computeRandomExpire(A arg) {
75+
long expire = (long) (Math.random() * 10000);
76+
return compute(arg, expire);
77+
}
78+
79+
public static void main(String[] args) {
80+
CacheUsage5<String, Integer> usage5 = new CacheUsage5<>(new ExpensiveFunc());
81+
new Thread(() -> {
82+
try {
83+
Integer result = usage5.computeRandomExpire("1222");
84+
System.out.println(Thread.currentThread().getName() + " result=" + result);
85+
} catch (Exception e) {
86+
e.printStackTrace();
87+
}
88+
}).start();
89+
new Thread(() -> {
90+
try {
91+
Integer result = usage5.compute("1222");
92+
System.out.println(Thread.currentThread().getName() + " result=" + result);
93+
} catch (Exception e) {
94+
e.printStackTrace();
95+
}
96+
}).start();
97+
new Thread(() -> {
98+
try {
99+
Integer result = usage5.compute("1222");
100+
System.out.println(Thread.currentThread().getName() + " result=" + result);
101+
} catch (Exception e) {
102+
e.printStackTrace();
103+
}
104+
}).start();
105+
}
106+
}

Cache/src/CacheUsage5Test.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import java.text.SimpleDateFormat;
2+
import java.util.Date;
3+
import java.util.concurrent.CountDownLatch;
4+
import java.util.concurrent.ExecutorService;
5+
import java.util.concurrent.Executors;
6+
7+
/**
8+
* 测试CacheUsage5
9+
*
10+
* @Author: zzStar
11+
* @Date: 10-13-2020 14:39
12+
*/
13+
public class CacheUsage5Test {
14+
15+
static CacheUsage5<String, Integer> cache = new CacheUsage5<>(new ExpensiveFunc());
16+
17+
public static void main(String[] args) throws InterruptedException {
18+
ExecutorService pool = Executors.newFixedThreadPool(100);
19+
//实现压测,同一时刻大量请求
20+
CountDownLatch countDownLatch = new CountDownLatch(1);
21+
for (int i = 0; i < 100; i++) {
22+
pool.submit(() -> {
23+
try {
24+
System.out.println(Thread.currentThread().getName() + "准备中.....");
25+
countDownLatch.await();
26+
System.out.println(Thread.currentThread().getName() + " 放行:" + TimeHolder.get().format(new Date()));
27+
Integer result = cache.compute("666");
28+
System.out.println("result=" + result);
29+
} catch (Exception e) {
30+
e.printStackTrace();
31+
}
32+
});
33+
}
34+
Thread.sleep(3000);
35+
countDownLatch.countDown();
36+
pool.shutdown();
37+
}
38+
}
39+
40+
class TimeHolder {
41+
//每个线程需要独享的对象
42+
private static ThreadLocal<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
43+
44+
public static SimpleDateFormat get() {
45+
return threadLocal.get();
46+
}
47+
}
48+

Cache/src/Computable.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* 提高代码复用性 —> 装饰者模式
3+
* 实现无侵入缓存功能
4+
*
5+
* @Author: zzStar
6+
* @Date: 10-13-2020 12:54
7+
*/
8+
public interface Computable<A,V> {
9+
V compute(A arg) throws Exception;
10+
}

0 commit comments

Comments
 (0)