WeakHashMap是啥:
WeakHashMap和HashMap都是通过"拉链法"实现的散列表。它们的源码绝大部分内容都一样,这里就只是对它们不同的部分就是说明。
WeakReference是“弱键”实现的哈希表。它这个“弱键”的目的就是:实现对“键值对”的动态回收。当“弱键”不再被使用到时,GC会回收它,WeakReference也会将“弱键”对应的键值对删除。
“弱键”是一个“弱引用(WeakReference)”,在Java中,WeakReference和ReferenceQueue 是联合使用的。在WeakHashMap中亦是如此:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 接着,WeakHashMap会根据“引用队列”,来删除“WeakHashMap中已被GC回收的‘弱键’对应的键值对”。
实际的应用:
可以使用WeakhashMap实现一个线程安全的基于LRU本地缓存
在Tomcat的工具类里,有这样一种实现。基于LRU策略,很巧妙。
Github :https://github.com/apache/tomcat/blob/3e5ce3108e2684bc25013d9a84a7966a6dcd6e14/java/org/apache/tomcat/util/collections/ConcurrentCache.java
WeakHashMap
package com.liruilong.common.util.concurrentcache;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* <per>
* <a>https://github.com/apache/tomcat/blob/3e5ce3108e2684bc25013d9a84a7966a6dcd6e14/java/org/apache/tomcat/util/collections/ConcurrentCache.java<a/>
* <per/>
* @Author https://github.com/apache/tomcat/blob/3e5ce3108e2684bc25013d9a84a7966a6dcd6e14/java/org/apache/tomcat/util/collections/ConcurrentCache.java
* @Date 2020/8/11 08:46
* @Description: org.apache.tomcat.util.collections;工具类,
* <p>基于WeakHashMap 实现线程安全的缓存</p>
*/
public final class ConcurrentCache<K,V> {
private final int size;
private final Map<K,V> eden;
private final Map<K,V> longterm;
public ConcurrentCache(int size){
this.size = size;
this.eden = new ConcurrentHashMap<>(size);
this.longterm = new WeakHashMap<>(size);
}
public V get(K k){
V v = this.eden.get(k);
if (Objects.isNull(v)){
synchronized (longterm){
v = this.longterm.get(k);
}
if (Objects.nonNull(v)){
this.eden.put(k,v);
}
}
return v;
}
public void put(K k,V v){
if (this.eden.size() >= size){
synchronized (longterm){
this.longterm.putAll(this.eden);
}
this.eden.clear();
}
this.eden.put(k,v);
}
}
在原有基础上,我做了改进,使用 volatile 和静态工厂的方式实现,只是个人想法,不足之处请小伙伴指出来。
package com.liruilong.common.util.concurrentcache;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author Liruilong
* @Date 2020/8/11 09:37
* @Description: 基于 WeakHashMap 的缓存实现
*/
public class WeakHashMapCache<K,V> {
private final int size;
private final Map<K,V> eden;
private final Map<K,V> longterm;
private WeakHashMapCache(Builder<K,V> builder){
this.size = builder.size;
this.eden = builder.eden;
this.longterm = builder.longterm;
}
public static class Builder<K,V>{
private volatile int size;
private volatile Map<K,V> eden;
private volatile Map<K,V> longterm;
public Builder(int size){
this.size = rangeCheck(size,Integer.MAX_VALUE,"缓存容器初始化容量异常");
this.eden = new ConcurrentHashMap<>(size);
this.longterm = new WeakHashMap<>(size);
}
private static int rangeCheck(int val, int i, String arg) {
if (val < 0 || val > i) {
throw new IllegalArgumentException(arg + ":" + val);
}
return val;
}
public WeakHashMapCache build(){
return new WeakHashMapCache(this);
}
}
public V get(K k){
V v = this.eden.get(k);
if (Objects.isNull(v)){
v = this.longterm.get(k);
if (Objects.nonNull(v)){
this.eden.put(k,v);
}
}
return v;
}
public void put(K k,V v){
if (this.eden.size() >= size){
this.longterm.putAll(this.eden);
this.eden.clear();
}
this.eden.put(k,v);
}
}
LinkedHashMap
当然,对于本地缓存,我们也可以使用 基于 LinkedHashMap 的实现缓存工具类,基于volatile 实现LRU策略线程安全缓存
package com.liruilong.common.util.concurrentcache;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Author Liruilong
* @Date 2020/8/11 14:19
* @Description: 基于 LinkedHashMap 的缓存实现
*/
public class LinkedHashMapCache<K,V> {
private final Map<K,V> eden;
public LinkedHashMapCache(Builder builder) {
this.eden = builder.eden;
}
public static class Builder<K,V> {
private volatile Map<K,V> eden;
private int size;
public Builder(int size){
this.size = rangeCheck(size,Integer.MAX_VALUE,"缓存容器初始化容量异常");
this.eden = new LinkedHashMap<K,V>(size,0.75f,true){
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() >= size;
}
};
}
private static int rangeCheck(int val, int i, String arg) {
if (val < 0 || val > i) {
throw new IllegalArgumentException(arg + ":" + val);
}
return val;
}
public LinkedHashMapCache build(){
return new LinkedHashMapCache(this);
}
}
public V get(K k){
return eden.get(k);
}
public void put(K k,V v){
this.eden.put(k,v);
}
public static void main(String[] args) {
LinkedHashMapCache cache = new LinkedHashMapCache.Builder<String,Integer>(3).build();
for (int i = 0; i < 5; i++) {
cache.put(i+"",i);
}
for (int i = 0; i < 5; i++) {
System.out.println(cache.get(i + ""));
}
}
}
测试文档
/**
* <b>package-info不是平常类,其作用有三个:</b><br>
* 1、为标注在包上Annotation提供便利;<br>
* 2、声明包的私有类和常量;<br>
* 3、提供包的整体注释说明。<br>
*/
/**
* <per>
* <p>1.ConcurrentCache: Tomcat中的一个缓存处理工具类,线程安全,基于WeakHashMap实现LRU策略缓存处理<p/>
* <p>2.LinkedHashMapCache:基于 LinkedHashMap 的缓存工具类,基于volatile 实现LRU策略线程安全缓存 <p/>
* <p>3.WeakHashMapCache: 基于 WeakHashMap 的缓存工具类,基于volatile 实现LRU策略线程安全缓存<p/>
* <per/>
* @Description 本地缓存工具包
* @author Liruilong
* @Date 2020年08月12日 11:08:19
**/
package com.liruilong.common.util.concurrentcache;
class DemoCache{
public static void main(String[] args) {
ConcurrentCache cache = new ConcurrentCache(3);
LinkedHashMapCache cache1 = new LinkedHashMapCache.Builder<String,Integer>(3).build();
WeakHashMapCache cache2 = new WeakHashMapCache.Builder<String,String>(3).build();
for (int i = 0; i < 5; i++) {
cache.put(i+"",i+"");
}
System.gc();
for (int i = 0; i < 5; i++) {
System.out.println(cache.get(i + ""));
}
}
}