前言
在实际项目中经常有一个需求,就是在本地将对象缓存起来,Java 里面常被作为缓存工具的数据结构就是 Map。关于 Collectors.toMap 的更多说明可以查看 Java 8 特性 - Collectors. toMap
缓存体系
- 首先定义个缓存键接口
public interface Cacheable<K> {
K getCachedKey();
}
- 其次构建一个对象类
public static class Person implements Cacheable<Integer> {
private Integer id;
private String name;
private Integer age;
public Person(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return String.format("Person[id = %d, name = %s, age = %d", id, name, age);
}
public Integer getCachedKey() {
return id;
}
}
- 再次,构建一个缓存类
import java.util.Map;
import java.util.HashMap;
public static class Cache {
// 为了保证缓存类的线程安全性,所以使用了 ThreadLocal
private static final ThreadLocal<Map<Object, Cacheable>> CACHE_HOLDER = ThreadLocal.withInitial(HashMap::new);
public static <K> Cacheable getCache(K key) {
return CACHE_HOLDER.get().get(key);
}
public static <K> void setCache(Cacheable<K> cachedObj) {
CACHE_HOLDER.get()
.put(cachedObj.getCachedKey(), cachedObj);
}
public static void clearCache() {
CACHE_HOLDER.remove();
}
public static <T> void print() {
for (Map.Entry<Object, Cacheable> entry : CACHE_HOLDER.get().entrySet()) {
System.out.println(entry);
}
}
}
- 最后构建一个测试类
public class CacheClassTest {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
try {
// 生成待缓存对象
Person person = new Person(1, "zhangsan", 21);
// 缓存
Cache.setCache(person);
// 访问缓存对象
System.out.println(Cache.getCache(person.getCachedKey()));
} finally {
Cache.clearCache();
}
});
Thread thread2 = new Thread(() -> {
try {
// 生成待缓存对象
Person person = new Person(1, "lisi", 21);
// 缓存
Cache.setCache(person);
// 访问缓存对象
System.out.println(Cache.getCache(person.getCachedKey()));
} finally {
Cache.clearCache();
}
});
thread1.start();
thread2.start();
}
}
运行结果为
可以看出上面的缓存体系能保证线程安全的前提下,正常工作。
缓存体系升级
上面的缓存体系只能对单个对象进行缓存,如果现在从数据库中读取了大量的数据,按照现有的 API ,其调用过程代码为:
public class CacheClassTest {
public static void main(String[] args) {
List<Cacheable<Integer>> people = new ArrayList<>();
people.add(new Person(1, "zhangsan", 21));
people.add(new Person(2, "lisi", 23));
// 遍历数据列表进行缓存
for (Person person : people) {
Cache.setCache(person);
}
Cache.print();
}
}
虽然这种迭代数据列表的写法可行,但这样做增加了用户的工作量,显然是不够优雅的,所以需要在缓存体系中增加批量缓存的功能。
新增批量缓存功能的缓存体系最终代码——新增代码主要是通过使用 Java 8 流式计算以及 Collectors.toMap 方法来实现的
public static class Cache {
// 为了保证缓存类的线程安全性,所以使用了 ThreadLocal
private static final ThreadLocal<Map<Object, Cacheable>> CACHE_HOLDER = ThreadLocal.withInitial(HashMap::new);
public static <K> Cacheable getCache(K key) {
return CACHE_HOLDER.get().get(key);
}
public static <K> void setCache(Cacheable<K> cachedObj) {
CACHE_HOLDER.get()
.put(cachedObj.getCachedKey(), cachedObj);
}
// 批量缓存
public static <K> void setCache(List<Cacheable<K>> cachedObjs) {
CACHE_HOLDER.get()
.putAll(cachedObjs.stream()
.collect(Collectors.toMap(Cacheable::getCachedKey,
Function.identity(),
(oldValue, newValue) -> newValue, HashMap::new)));
}
public static void clearCache() {
CACHE_HOLDER.remove();
}
public static <T> void print() {
for (Map.Entry<Object, Cacheable> entry : CACHE_HOLDER.get().entrySet()) {
System.out.println(entry);
}
}
}
测试代码为
public class CacheClassTest {
public static void main(String[] args) {
List<Cacheable<Integer>> people = new ArrayList<>();
people.add(new Person(1, "zhangsan", 21));
people.add(new Person(2, "lisi", 23));
Cache.setCache(people);
Cache.print();
}
}
测试结果为