Skip to content

Commit c113ab4

Browse files
add module [demo-java-threadSafeCache]
Signed-off-by: ooooo <[email protected]>
1 parent 3d4df33 commit c113ab4

File tree

14 files changed

+222
-3
lines changed

14 files changed

+222
-3
lines changed

spring-boot-annotationProcessor/build.gradle renamed to demo-java-annotationProcessor/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ dependencyManagement {
55
}
66

77
dependencies {
8-
// annotationProcessor project(":spring-boot-annotationProcessor")
9-
// testAnnotationProcessor project(":spring-boot-annotationProcessor")
8+
// annotationProcessor project(":demo-java-annotationProcessor")
9+
// testAnnotationProcessor project(":demo-java-annotationProcessor")
1010
implementation(files("${System.properties['java.home']}/../lib/tools.jar"))
1111

1212
testImplementation('com.google.testing.compile:compile-testing:0.19')
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies {
2+
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.ooooo.cache;
2+
3+
import java.util.function.Supplier;
4+
5+
/**
6+
* @author <a href="https://github.com/ooooo-youwillsee">ooooo</a>
7+
* @since 1.0.0
8+
*/
9+
public interface Cache<T> {
10+
11+
T get(String key);
12+
13+
void put(String key, Supplier<T> supplier);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.ooooo.cache.impl;
2+
3+
import com.ooooo.cache.Cache;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
import java.util.function.Supplier;
7+
8+
/**
9+
* @author <a href="https://github.com/ooooo-youwillsee">ooooo</a>
10+
* @since 1.0.0
11+
*/
12+
public class SafeCacheImpl1<T> implements Cache<T> {
13+
14+
private final Map<String, T> map = new HashMap<>();
15+
16+
@Override
17+
public synchronized T get(String key) {
18+
return map.get(key);
19+
}
20+
21+
/**
22+
* 因为使用 synchronized, 导致性能太低了
23+
* 比如 A -> AValue, B -> BValue, 这两个操作应该是互不影响的,
24+
* 但是现在锁是同一个,所以性能不高
25+
*
26+
* @param key
27+
* @param supplier
28+
*/
29+
@Override
30+
public synchronized void put(String key, Supplier<T> supplier) {
31+
if (!map.containsKey(key)) {
32+
map.put(key, supplier.get());
33+
}
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.ooooo.cache.impl;
2+
3+
import com.ooooo.cache.Cache;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
import java.util.function.Supplier;
7+
8+
/**
9+
* @author <a href="https://github.com/ooooo-youwillsee">ooooo</a>
10+
* @since 1.0.0
11+
*/
12+
public class SafeCacheImpl2<T> implements Cache<T> {
13+
14+
private final Map<String, T> map = new HashMap<>();
15+
16+
@Override
17+
public synchronized T get(String key) {
18+
return map.get(key);
19+
}
20+
21+
/**
22+
* 因为使用 synchronized, 导致性能太低了
23+
* 比如 A -> AValue, B -> BValue, 这两个操作应该是互不影响的,
24+
* 但是现在锁是同一个,所以性能不高
25+
*
26+
* @param key
27+
* @param supplier
28+
*/
29+
@Override
30+
public void put(String key, Supplier<T> supplier) {
31+
if (!map.containsKey(key)) {
32+
synchronized (this) {
33+
if (!map.containsKey(key)) {
34+
map.put(key, supplier.get());
35+
}
36+
}
37+
}
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.ooooo.cache.impl;
2+
3+
import com.ooooo.cache.Cache;
4+
import java.util.Map;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.function.Supplier;
7+
8+
/**
9+
* @author <a href="https://github.com/ooooo-youwillsee">ooooo</a>
10+
* @since 1.0.0
11+
*/
12+
public class SafeCacheImpl3<T> implements Cache<T> {
13+
14+
private final Map<String, T> map = new ConcurrentHashMap<>();
15+
16+
@Override
17+
public T get(String key) {
18+
return map.get(key);
19+
}
20+
21+
@Override
22+
public void put(String key, Supplier<T> supplier) {
23+
// 每次都会计算 supplier.get(), 导致重复计算
24+
// map.putIfAbsent(key, supplier.get());
25+
26+
// 存在竞态条件,所有会导致 supplier.get() 会被重复计算, value 值不一样
27+
// if (!map.containsKey(key)) {
28+
// map.put(key, supplier.get());
29+
// }
30+
31+
// 最好的做法
32+
map.computeIfAbsent(key, __ -> supplier.get());
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.ooooo.cache.impl;
2+
3+
import com.ooooo.cache.Cache;
4+
import java.util.Map;
5+
import java.util.concurrent.ConcurrentHashMap;
6+
import java.util.concurrent.atomic.AtomicBoolean;
7+
import java.util.function.Supplier;
8+
9+
/**
10+
* @author <a href="https://github.com/ooooo-youwillsee">ooooo</a>
11+
* @since 1.0.0
12+
*/
13+
public class SafeCacheImpl4<T> implements Cache<T> {
14+
15+
private final Map<String, ValueHolder<T>> map = new ConcurrentHashMap<>();
16+
17+
@Override
18+
public T get(String key) {
19+
return map.get(key).get();
20+
}
21+
22+
@Override
23+
public void put(String key, Supplier<T> supplier) {
24+
if (map.containsKey(key)) {
25+
return;
26+
}
27+
28+
ValueHolder<T> value = new ValueHolder<>(supplier);
29+
ValueHolder<T> targetValue = map.putIfAbsent(key, value);
30+
if (targetValue == null) {
31+
targetValue = value;
32+
// 初始化调用
33+
targetValue.get();
34+
}
35+
}
36+
37+
private static class ValueHolder<T> {
38+
39+
private final AtomicBoolean init = new AtomicBoolean(false);
40+
41+
private volatile T value;
42+
43+
private Supplier<T> supplier;
44+
45+
private ValueHolder(Supplier<T> supplier) {
46+
this.supplier = supplier;
47+
}
48+
49+
T get() {
50+
if (init.compareAndSet(false, true)) {
51+
value = supplier.get();
52+
supplier = null;
53+
}
54+
55+
return value;
56+
}
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.ooooo.cache.impl;
2+
3+
import com.ooooo.cache.Cache;
4+
import java.util.HashMap;
5+
import java.util.Map;
6+
import java.util.function.Supplier;
7+
8+
/**
9+
* @author <a href="https://github.com/ooooo-youwillsee">ooooo</a>
10+
* @since 1.0.0
11+
*/
12+
public class UnsafeCacheImpl<T> implements Cache<T> {
13+
14+
private final Map<String, T> map = new HashMap<>();
15+
16+
@Override
17+
public T get(String key) {
18+
return map.get(key);
19+
}
20+
21+
/**
22+
* 两个线程会同时去执行 put 方法,所以线程不安全
23+
*
24+
* @param key
25+
* @param supplier
26+
*/
27+
@Override
28+
public void put(String key, Supplier<T> supplier) {
29+
if (!map.containsKey(key)) {
30+
// 两个线程都会执行这里,所以线程不安全
31+
map.put(key, supplier.get());
32+
}
33+
}
34+
}

settings.gradle

+3-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ include "spring-boot-methodHandler"
5353

5454
include "spring-boot-grpc"
5555

56-
include "spring-boot-annotationProcessor"
56+
include "demo-java-annotationProcessor"
57+
58+
include "demo-java-threadSafeCache"
5759

5860
// spring boot examples
5961
include "spring-boot-examples:spring-boot-redis"

0 commit comments

Comments
 (0)