1、问题描述:
采用ConcurrentLruCache 作为JVM缓存池对象
public class UserManager {
private ConcurrentLruCache<UserCacheKey, UserInfo> cache = new ConcurrentLruCache<UserCacheKey, UserInfo>(100, new Function<UserCacheKey, UserInfo>() {
@Override
public UserInfo apply(UserCacheKey key) {
return loadUserInfo(key.getUserid(), key.getActivity());
}
});
public UserInfo findByPortalId(String userid, String activityname) {
UserInfo info = null;
try {
info = cache.get(generaterCacheKey(userid, activityname));
} catch (Exception e) {
GFLogUtil.i(userid + activityname + " is null");
}
return info;
}
private UserCacheKey generaterCacheKey(String userid, String activityname) {
return new UserCacheKey(userid, activityname);
}
}
从上面的代码段可以看到,缓存的KEY值竟是一个自定义对象类型,如下所示:
public class UserCacheKey {
private String userid = "";
private String activity = "";
public UserCacheKey(String userid, String activity) {
this.userid = userid;
this.activity = activity;
}
public String getUserid() {
return userid;
}
public String getActivity() {
return activity;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UserCacheKey) {
UserCacheKey key = (UserCacheKey) obj;
return userid.equals(key.getUserid()) && activity.equals(key.getActivity());
}
return false;
}
}
以上代码展示了缓存KEY值对象内部结构,还是比较简单:
(1)定义了2个成员变量,用来作为从数据库检索数据的条件参数。
(2)重写了equals方式,用来把一个对象添加到缓存池中的时候,根据内容判定是否存在相同对象。
2、问题分析:
通过第1步的操作,表面上看起来没有什么问题,而实际使用中发现缓存KEY值并没有直接命中,每次查询出的对象都是走的DB查询。
为什么传入对象的属性值即便是相同的,而缓存池对象却每次都以为是新对象呢?带着这个问题,在深入ConcurrentLruCache本身去看就会发现,其内部采用的是ConcurrentHashMap实现存储机制,既然有HashMap的影子,那问题就比较明朗了。因为HashMap存储对象的时候,首先比较的是对象的HashCode值,如果HashCode值相同的情况下,才在调用对象的equals方法比较对象的内容。
到此为止,我想问题已经知道出在UserCacheKey 本身了,在该类中忽略了一个很重要的方法,就是我们经常听到的一句话:“重写类的equals方式,最好也重写类的hashCode方法”
3、问题解决:
修改之后的UserCacheKey 如下所示:
public class UserCacheKey {
private String userid = "";
private String activity = "";
public UserCacheKey(String userid, String activity) {
this.userid = userid;
this.activity = activity;
}
public String getUserid() {
return userid;
}
public String getActivity() {
return activity;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UserCacheKey) {
UserCacheKey key = (UserCacheKey) obj;
return userid.equals(key.getUserid()) && activity.equals(key.getActivity());
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(userid, activity);
}
}
问题总结:
1、对于一个java对象,其hashCode方法的重要性,它是判定俩对象是否相同的首选判定依据;
2、加深对HashMap存储机制的了解,它先比较俩对象的hashCode,在比较对象的equals内容,从而决定是否存储该对象。