LinkedHashMap实现LRU以及其在spring-mvc中AbstractCachingViewResolver运用

LinkedHashMap实现LRU以及其在spring-mvc中AbstractCachingViewResolver运用

一、LinkedHashMap实现LRU

1、LinkedHashMap有一个三个参数的构造函数,new LinkedHashMap<Object, View>(size, 0.75f, true),其中第三个参数accessOrder的作用是如果元素被访问的情况下,是否把元素添加到链表的尾部。结合LinkedHashMap的一个protected的removeEldestEntry方法,可以实现LRU(即最久没访问的移除)。

LinkedHashMap<Object,Object> map = new LinkedHashMap<Object, Object>(100, 0.75f, true);
		map.put("1", "a");
		map.put("2", "b");
		map.put("3", "c");
		map.put("4", "d");
		for (Iterator<Object> iterator = map.values().iterator(); iterator.hasNext();) {
			String name = (String) iterator.next();
			System.out.print(name); // 默认是abcd
		}
		System.out.println("........................................................");
		map.get("1");
		map.get("2");
		System.out.println(map);
		for (Iterator<Object> iterator = map.values().iterator(); iterator.hasNext();) {
			String name = (String) iterator.next();
			System.out.print(name); // 变成了cdab 即被访问后,元素的顺序被放到了最后面。
		}

从上面我们可以看到,元素添加abcd,如果没有使用下面的get方法,那么遍历的时候,结果也是abcd,但是get元素后,元素被移动到尾部,即等于这个元素移动到最新添加的位置,那么最后的结果就变成了cdab。

2、LinkedHashMap如何实现LRU,LRU是缓存淘汰策略里的一种,即最久没有使用进行移除,上面说的removeEldestEntry方法,如果返回ture,并且当容器的size大于初始给定的size时,那么就会移除掉链表的最前面的元素。看下面的案例。

// 如果对LinkedHashMap的removeEldestEntry方法进行复写,即大于容量,进行移除最先插入的。
		LinkedHashMap<Object,Object> map2 = new LinkedHashMap<Object, Object>(20, 0.75f, true){
			protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
				if (size() > 20) {
					return true;
				}else {
					return false;
				}
			}
		};
		for (int i = 0; i < 30; i++) {
			map2.put(i+"", i);
			if(i>15) {
				map2.get(i+"");
			}
		}
		// 因为最后添加的15个元素持续被添加到map的结尾,那么最先添加10个元素就会被移除。
		System.out.println(map2);

二、AbstractCachingViewResolver中的运用

AbstractCachingViewResolver是用来创建View的抽象基类,其创建功能是由子类模板方法实现的,它的主要功能就是缓存创建的View,但是为了不会无限的创建View,占用内存空间,这个类就利用LinkedHashMap的三个构造函数,在缓存1000个view后,会自动移除最久没访问的View对象。

细节实现是其内部有二个map变量,其中一个ConcurrentHashMap的实现用来快速读取view,如果我们无限缓存,就不必要引入另外一个LinkedHashMap了,另外一个map的作用就是同步创建更新删除View,并且LinkedHashMap加锁更新的时候也对ConcurrentHashMap进行了更新,这样我们就达到了利用一个非线程安全的LinkedHashMap利用加锁操作实现LRU的同时,对只提供访问的ConcurrentHashMap也进行了LRU的移除操作。

代码演示:

/**
	 * MyCacheViewResolve的演示
	 * 创建30个view,当容量大于限制20个时,会进行移除,而viewCreationCache只有put操作访问,没有获取操作
	 * 即是移除最先添加的,最终结果就是10-29的view存在。
	 */
	public static void main(String[] args) throws Exception {
		MyCacheViewResolve resolve = new MyCacheViewResolve();
		for (int i = 0; i < 30; i++) {
			resolve.resolveViewName("view"+i);
		}
		System.out.println(resolve); // 10-29存在。
	}
}

class MyCacheViewResolve {
	public static final int DEFAULT_CACHE_LIMIT = 20;
	private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
	private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT);
	@SuppressWarnings("serial")
	private final Map<Object, View> viewCreationCache = new LinkedHashMap<Object, View>(DEFAULT_CACHE_LIMIT, 0.75f, true) {
		@Override
		protected boolean removeEldestEntry(Map.Entry<Object, View> eldest) {
			if (size() > cacheLimit) { // 大于cachelimit时,reutrn true,能删除自身的,并且额外移除viewAccessCache的
				viewAccessCache.remove(eldest.getKey());
				return true;
			} else {
				return false;
			}
		}
	};

	@Override
	public String toString() {
		return "MyCacheViewResolve [viewAccessCache=" + viewCreationCache + "]";
	}

	public View resolveViewName(String viewName) throws Exception {
		View view = this.viewAccessCache.get(viewName); // 从concurrentMap缓存中取
		if (view == null) {
			synchronized (this.viewCreationCache) { // 空就同步双检CreateMap创建
				view = this.viewCreationCache.get(viewName); //
				if (view == null) {
					view = createView(viewName);
					if (view != null) {
						this.viewAccessCache.put(viewName, view);
						this.viewCreationCache.put(viewName, view);
					}
				}
			}
		}
		return view;
	}

	private View createView(String viewName) {
		return new View(viewName);
	}

	public void removeFromCache(String viewName) {
		synchronized (this.viewCreationCache) {
			this.viewAccessCache.remove(viewName);
			this.viewCreationCache.remove(viewName);
		}
	}

}

class View {
	private String name;
	public View(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "View [name=" + name + "]";
	}
}

猜你喜欢

转载自blog.csdn.net/shuixiou1/article/details/113006428