所谓缓存,就是将程序或系统常常要调用的对象存在内存中,一遍其使用时能够快速调用,没必要再去建立新的重复的实例。这样作能够减小系统开销,提升系统效率。 java
AD:WOT2015 互联网运维与开发者大会 热销抢票 算法
Cache 数据库
所谓缓存,就是将程序或系统常常要调用的对象存在内存中,一遍其使用时能够快速调用,没必要再去建立新的重复的实例。这样作能够减小系统开销,提升系统效率。 缓存
缓存主要可分为二大类: 安全
1、经过文件缓存,顾名思义文件缓存是指把数据存储在磁盘上,无论你是以XML格式,序列化文件DAT格式仍是其它文件格式; 网络
2、内存缓存,也就是实现一个类中静态Map,对这个Map进行常规的增删查. 数据结构
代码以下 : 多线程
- package lhm.hcy.guge.frameset.cache;
- import java.util.*;
- //Description: 管理缓存
- //可扩展的功能:当chche到内存溢出时必须清除掉最先期的一些缓存对象,这就要求对每一个缓存对象保存建立时间
- public class CacheManager {
- private static HashMap cacheMap = new HashMap();
- //单实例构造方法
- private CacheManager() {
- super();
- }
- //获取布尔值的缓存
- public static boolean getSimpleFlag(String key){
- try{
- return (Boolean) cacheMap.get(key);
- }catch(NullPointerException e){
- return false;
- }
- }
- public static long getServerStartdt(String key){
- try {
- return (Long)cacheMap.get(key);
- } catch (Exception ex) {
- return 0;
- }
- }
- //设置布尔值的缓存
- public synchronized static boolean setSimpleFlag(String key,boolean flag){
- if (flag && getSimpleFlag(key)) {//假如为真不容许被覆盖
- return false;
- }else{
- cacheMap.put(key, flag);
- return true;
- }
- }
- public synchronized static boolean setSimpleFlag(String key,long serverbegrundt){
- if (cacheMap.get(key) == null) {
- cacheMap.put(key,serverbegrundt);
- return true;
- }else{
- return false;
- }
- }
- //获得缓存。同步静态方法
- private synchronized static Cache getCache(String key) {
- return (Cache) cacheMap.get(key);
- }
- //判断是否存在一个缓存
- private synchronized static boolean hasCache(String key) {
- return cacheMap.containsKey(key);
- }
- //清除全部缓存
- public synchronized static void clearAll() {
- cacheMap.clear();
- }
- //清除某一类特定缓存,经过遍历HASHMAP下的全部对象,来判断它的KEY与传入的TYPE是否匹配
- public synchronized static void clearAll(String type) {
- Iterator i = cacheMap.entrySet().iterator();
- String key;
- ArrayList arr = new ArrayList();
- try {
- while (i.hasNext()) {
- java.util.Map.Entry entry = (java.util.Map.Entry) i.next();
- key = (String) entry.getKey();
- if (key.startsWith(type)) { //若是匹配则删除掉
- arr.add(key);
- }
- }
- for (int k = 0; k < arr.size(); k++) {
- clearOnly(arr.get(k));
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- //清除指定的缓存
- public synchronized static void clearOnly(String key) {
- cacheMap.remove(key);
- }
- //载入缓存
- public synchronized static void putCache(String key, Cache obj) {
- cacheMap.put(key, obj);
- }
- //获取缓存信息
- public static Cache getCacheInfo(String key) {
- if (hasCache(key)) {
- Cache cache = getCache(key);
- if (cacheExpired(cache)) { //调用判断是否终止方法
- cache.setExpired(true);
- }
- return cache;
- }else
- return null;
- }
- //载入缓存信息
- public static void putCacheInfo(String key, Cache obj, long dt,boolean expired) {
- Cache cache = new Cache();
- cache.setKey(key);
- cache.setTimeOut(dt + System.currentTimeMillis()); //设置多久后更新缓存
- cache.setValue(obj);
- cache.setExpired(expired); //缓存默认载入时,终止状态为FALSE
- cacheMap.put(key, cache);
- }
- //重写载入缓存信息方法
- public static void putCacheInfo(String key,Cache obj,long dt){
- Cache cache = new Cache();
- cache.setKey(key);
- cache.setTimeOut(dt+System.currentTimeMillis());
- cache.setValue(obj);
- cache.setExpired(false);
- cacheMap.put(key,cache);
- }
- //判断缓存是否终止
- public static boolean cacheExpired(Cache cache) {
- if (null == cache) { //传入的缓存不存在
- return false;
- }
- long nowDt = System.currentTimeMillis(); //系统当前的毫秒数
- long cacheDt = cache.getTimeOut(); //缓存内的过时毫秒数
- if (cacheDt <= 0||cacheDt>nowDt) { //过时时间小于等于零时,或者过时时间大于当前时间时,则为FALSE
- return false;
- } else { //大于过时时间 即过时
- return true;
- }
- }
- //获取缓存中的大小
- public static int getCacheSize() {
- return cacheMap.size();
- }
- //获取指定的类型的大小
- public static int getCacheSize(String type) {
- int k = 0;
- Iterator i = cacheMap.entrySet().iterator();
- String key;
- try {
- while (i.hasNext()) {
- java.util.Map.Entry entry = (java.util.Map.Entry) i.next();
- key = (String) entry.getKey();
- if (key.indexOf(type) != -1) { //若是匹配则删除掉
- k++;
- }
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- return k;
- }
- //获取缓存对象中的全部键值名称
- public static ArrayList getCacheAllkey() {
- ArrayList a = new ArrayList();
- try {
- Iterator i = cacheMap.entrySet().iterator();
- while (i.hasNext()) {
- java.util.Map.Entry entry = (java.util.Map.Entry) i.next();
- a.add((String) entry.getKey());
- }
- } catch (Exception ex) {} finally {
- return a;
- }
- }
- //获取缓存对象中指定类型 的键值名称
- public static ArrayList getCacheListkey(String type) {
- ArrayList a = new ArrayList();
- String key;
- try {
- Iterator i = cacheMap.entrySet().iterator();
- while (i.hasNext()) {
- java.util.Map.Entry entry = (java.util.Map.Entry) i.next();
- key = (String) entry.getKey();
- if (key.indexOf(type) != -1) {
- a.add(key);
- }
- }
- } catch (Exception ex) {} finally {
- return a;
- }
- }
- }
- package lhm.hcy.guge.frameset.cache;
- public class Cache {
- private String key;//缓存ID
- private Object value;//缓存数据
- private long timeOut;//更新时间
- private boolean expired; //是否终止
- public Cache() {
- super();
- }
- public Cache(String key, Object value, long timeOut, boolean expired) {
- this.key = key;
- this.value = value;
- this.timeOut = timeOut;
- this.expired = expired;
- }
- public String getKey() {
- return key;
- }
- public long getTimeOut() {
- return timeOut;
- }
- public Object getValue() {
- return value;
- }
- public void setKey(String string) {
- key = string;
- }
- public void setTimeOut(long l) {
- timeOut = l;
- }
- public void setValue(Object object) {
- value = object;
- }
- public boolean isExpired() {
- return expired;
- }
- public void setExpired(boolean b) {
- expired = b;
- }
- }
- //测试类,
- class Test {
- public static void main(String[] args) {
- System.out.println(CacheManager.getSimpleFlag("alksd"));
- // CacheManager.putCache("abc", new Cache());
- // CacheManager.putCache("def", new Cache());
- // CacheManager.putCache("ccc", new Cache());
- // CacheManager.clearOnly("");
- // Cache c = new Cache();
- // for (int i = 0; i < 10; i++) {
- // CacheManager.putCache("" + i, c);
- // }
- // CacheManager.putCache("aaaaaaaa", c);
- // CacheManager.putCache("abchcy;alskd", c);
- // CacheManager.putCache("cccccccc", c);
- // CacheManager.putCache("abcoqiwhcy", c);
- // System.out.println("删除前的大小:"+CacheManager.getCacheSize());
- // CacheManager.getCacheAllkey();
- // CacheManager.clearAll("aaaa");
- // System.out.println("删除后的大小:"+CacheManager.getCacheSize());
- // CacheManager.getCacheAllkey();
- }
- }
java中的本地缓存,工做后陆续用到,一直想写,一直无从下手,最近又涉及到这方面的问题了,梳理了一下。本身构造单例、guava、ehcache基本上涵盖了目前的大多数行为了。为何要有本地缓存?在系统中,有些数据,数据量小,可是访问十分频繁(例如国家标准行政区域数据),针对这种场景,须要将数据搞到应用的本地缓存中,以提高系统的访问效率, 减小无谓的数据库访问(数据库访问占用数据库链接,同时网络消耗比较大),可是有一点须要注意,就是缓存的占用空间以及缓存的失效策略。为何是本地缓存,而不是分布式的集群缓存?目前的数据,大可能是业务无关的小数据缓存,没有必要搞分布式的集群缓存,目前涉及到订单和商品的数据,会直接走DB进行请求,再加上分布式缓存的构建,集群维护成本比较高,不太适合紧急的业务项目。这里介绍一下缓存使用的三个阶段(摘自info架构师文档)本地缓存在那个区域?目前考虑的是占用了JVM的heap区域,再细化一点的就是heap中的old区,目前的数据量来看,都是一些小数据,加起来没有几百兆,放 在heap区域最快最方便。后期若是须要放置在本地缓存的数据大的时候,能够考虑在off-heap区域(direct-memory 或者 big-memory),可是off-heap区域的话,须要考虑对象的序列化(由于off-heap区域存储的是二进制的数据),另一个的话就是 off-heap的GC问题。其实,若是真的数据量比较大,那其实就能够考虑搞一个集中式的缓存系统,能够是单机,也能够是集群,来承担缓存的做用。搞一个单例模式,里面有个Map的变量来放置数据关于单例模式,一个既简单又复杂的模式(http://iamzhongyong.iteye.com/blog/1539642)很是典型的代码以下:public class SingletonMap {//一个本地的缓存Mapprivate Map localCacheStore = new HashMap();//一个私有的对象,非懒汉模式private static SingletonMap singletonMap = new SingletonMap();//私有构造方法,外部不能够new一个对象private SingletonMap(){}//静态方法,外部得到实例对象public static SingletonMap getInstance(){return singletonMap;}//得到缓存中的数据public Object getValueByKey(String key){return localCacheStore.get(key);}//向缓存中添加数据public void putValue(String key , Object value){localCacheStore.put(key, value);}}这种能不能用?能够用,可是很是局限可是这种的就是本地缓存了吗?答案显然不是,为啥呢?一、 没有缓存大小的设置,没法限定缓存体的大小以及存储数据的限制(max size limit);二、 没有缓存的失效策略(eviction policies);三、 没有弱键引用,在内存占用吃紧的状况下,JVM是没法回收的(weak rererences keys);四、 没有监控统计(statistics);五、 持久性存储(persistent store);因此,这种就直接废掉了。。。引入EhCache来构建缓存(详细介绍: http://raychase.iteye.com/blog/1545906)EhCahce的核心类:A、CacheManager:Cache的管理类;B、Cache:具体的cache类信息,负责缓存的get和put等操做C、CacheConfiguration :cache的配置信息,包含策略、最大值等信息D、Element:cache中单条缓存数据的单位典型的代码以下:public static void main(String[] args) {//EhCache的缓存,是经过CacheManager来进行管理的CacheManager cacheManager = CacheManager.getInstance();//缓存的配置,也能够经过xml文件进行CacheConfiguration conf = new CacheConfiguration();conf.name("cache_name_default");//设置名字conf.maxEntriesLocalHeap(1000);//最大的缓存数量conf.memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.LRU);//设置失效策略//建立一个缓存对象,并把设置的信息传入进去Cache localCache = new Cache(conf);//将缓存对象添加到管理器中cacheManager.addCache(localCache);localCache.put(new Element("iamzhongyong", new Date()));System.out.println(localCache.getSize());System.out.println(localCache.getStatistics().toString());System.out.println(localCache.getName());System.out.println(localCache.get("iamzhongyong").toString());System.out.println(localCache.get("iamzhongyong").getObjectValue());}固然,Cache的配置信息,能够经过配置文件制定了。。。优势:功能强大,有失效策略、最大数量设置等,缓存的持久化只有企业版才有,组件的缓存同步,能够经过jgroup来实现缺点:功能强大的同时,也使其更加复杂引入guava的cacheBuilder来构建缓存这个很是强大、简单,经过一个CacheBuilder类就能够知足需求。缺点就是若是要组件同步的话,须要本身实现这个功能。典型的代码以下:public class GuavaCacheBuilderTest {public static void main(String[] args) throws Exception{GuavaCacheBuilderTest cache = new GuavaCacheBuilderTest();cache.getNameLoadingCache("bixiao");}public void getNameLoadingCache(String name) throws Exception{LoadingCache cache = CacheBuilder.newBuilder().maximumSize(20)//设置大小,条目数.expireAfterWrite(20, TimeUnit.SECONDS)//设置失效时间,建立时间.expireAfterAccess(20, TimeUnit.HOURS) //设置时效时间,最后一次被访问.removalListener(new RemovalListener() { //移除缓存的监听器public void onRemoval(RemovalNotification notification) {System.out.println("有缓存数据被移除了");}}).build(new CacheLoader(){ //经过回调加载缓存@Overridepublic String load(String name) throws Exception {return name + "-" + "iamzhongyong";}});System.out.println(cache.get(name));//cache.invalidateAll();}}缓存预热怎么搞?A、全量预热,固定的时间段移除全部,而后再全量预热适用场景:一、数据更新不频繁,例如天天晚上3点更新便可的需求;二、数据基本没有变化,例如全国区域性数据;B、增量预热(缓存查询,没有,则查询数据库,有则放入缓存)适用场景:一、 数据更新要求缓存中同步更新的场景集群内部,缓存的一致性如何保证?若是采用ehcache的话,可使用框架自己的JGroup来实现组内机器之间的缓存同步。若是是采用google的cacheBuilder的话,须要本身实现缓存的同步。A、非实时生效数据:数据的更新不会时时发生,应用启动的时候更新便可,而后定时程序定时去清理缓存;B、须要实时生效数据:启动时可预热也可不预热,可是缓存数据变动后,集群之间须要同步LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存必定量的数据,当超过设定的阈值时就把一些过时的数据删除掉, 好比咱们缓存10000条数据,当数据小于10000时能够随意添加,当超过10000时就须要把新的数据添加进来,同时要把过时数据删除,以确保咱们最 大缓存10000条,那怎么肯定删除哪条过时数据呢,采用LRU算法实现的话就是将最老的数据删掉,废话很少说,下面来讲下Java版的LRU缓存实现 架构
Java里面实现LRU缓存一般有两种选择,一种是使用LinkedHashMap,一种是本身设计数据结构,使用链表+HashMap app
LRU Cache的LinkedHashMap实现
LinkedHashMap自身已经实现了顺序存储,默认状况下是按照元素的添加顺序存储,也能够启用按照访问顺序存储,即最近读取的数据放在最前 面,最先读取的数据放在最后面,而后它还有一个判断是否删除最老数据的方法,默认是返回false,即不删除数据,咱们使用LinkedHashMap实 现LRU缓存的方法就是对LinkedHashMap实现简单的扩展,扩展方式有两种,一种是inheritance,一种是delegation,具体 使用什么方式看我的喜爱
//LinkedHashMap的一个构造函数,当参数accessOrder为true时,即会按照访问顺序排序,最近访问的放在最前,最先访问的放在后面 public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; } //LinkedHashMap自带的判断是否删除最老的元素方法,默认返回false,即不删除老数据 //咱们要作的就是重写这个方法,当知足必定条件时删除老数据 protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }LRU缓存LinkedHashMap(inheritance)实现
采用inheritance方式实现比较简单,并且实现了Map接口,在多线程环境使用时可使用 Collections.synchronizedMap()方法实现线程安全操做
package cn.lzrabbit.structure.lru; import java.util.LinkedHashMap; import java.util.Map; /** * Created by liuzhao on 14-5-15. */ public class LRUCache2<K, V> extends LinkedHashMap<K, V> { private final int MAX_CACHE_SIZE; public LRUCache2(int cacheSize) { super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); MAX_CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Map.Entry<K, V> entry : entrySet()) { sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue())); } return sb.toString(); } }这样算是比较标准的实现吧,实际使用中这样写仍是有些繁琐,更实用的方法时像下面这样写,省去了单独见一个类的麻烦
final int cacheSize = 100; Map<String, String> map = new LinkedHashMap<String, String>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) { @Override protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { return size() > cacheSize; } };
LRU缓存LinkedHashMap(delegation)实现
delegation方式实现更加优雅一些,可是因为没有实现Map接口,因此线程同步就须要本身搞定了
package cn.lzrabbit.structure.lru; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** * Created by liuzhao on 14-5-13. */ public class LRUCache3<K, V> { private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTOR = 0.75f; LinkedHashMap<K, V> map; public LRUCache3(int cacheSize) { MAX_CACHE_SIZE = cacheSize; //根据cacheSize和加载因子计算hashmap的capactiy,+1确保当达到cacheSize上限时不会触发hashmap的扩容, int capacity = (int) Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTOR) + 1; map = new LinkedHashMap(capacity, DEFAULT_LOAD_FACTOR, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } }; } public synchronized void put(K key, V value) { map.put(key, value); } public synchronized V get(K key) { return map.get(key); } public synchronized void remove(K key) { map.remove(key); } public synchronized Set<Map.Entry<K, V>> getAll() { return map.entrySet(); } public synchronized int size() { return map.size(); } public synchronized void clear() { map.clear(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : map.entrySet()) { sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue())); } return sb.toString(); } }LRU Cache的链表+HashMap实现
注:此实现为非线程安全,若在多线程环境下使用须要在相关方法上添加synchronized以实现线程安全操做
package cn.lzrabbit.structure.lru; import java.util.HashMap; /** * Created by liuzhao on 14-5-12. */ public class LRUCache1<K, V> { private final int MAX_CACHE_SIZE; private Entry first; private Entry last; private HashMap<K, Entry<K, V>> hashMap; public LRUCache1(int cacheSize) { MAX_CACHE_SIZE = cacheSize; hashMap = new HashMap<K, Entry<K, V>>(); } public void put(K key, V value) { Entry entry = getEntry(key); if (entry == null) { if (hashMap.size() >= MAX_CACHE_SIZE) { hashMap.remove(last.key); removeLast(); } entry = new Entry(); entry.key = key; } entry.value = value; moveToFirst(entry); hashMap.put(key, entry); } public V get(K key) { Entry<K, V> entry = getEntry(key); if (entry == null) return null; moveToFirst(entry); return entry.value; } public void remove(K key) { Entry entry = getEntry(key); if (entry != null) { if (entry.pre != null) entry.pre.next = entry.next; if (entry.next != null) entry.next.pre = entry.pre; if (entry == first) first = entry.next; if (entry == last) last = entry.pre; } hashMap.remove(key); } private void moveToFirst(Entry entry) { if (entry == first) return; if (entry.pre != null) entry.pre.next = entry.next; if (entry.next != null) entry.next.pre = entry.pre; if (entry == last) last = last.pre; if (first == null || last == null) { first = last = entry; return; } entry.next = first; first.pre = entry; first = entry; entry.pre = null; } private void removeLast() { if (last != null) { last = last.pre; if (last == null) first = null; else last.next = null; } } private Entry<K, V> getEntry(K key) { return hashMap.get(key); } @Override public String toString() { StringBuilder sb = new StringBuilder(); Entry entry = first; while (entry != null) { sb.append(String.format("%s:%s ", entry.key, entry.value)); entry = entry.next; } return sb.toString(); } class Entry<K, V> { public Entry pre; public Entry next; public K key; public V value; } }LinkedHashMap的FIFO实现
FIFO是First Input First Output的缩写,也就是常说的先入先出,默认状况下LinkedHashMap就是按照添加顺序保存,咱们只需重写下removeEldestEntry方法便可轻松实现一个FIFO缓存,简化版的实现代码以下
final int cacheSize = 5; LinkedHashMap<Integer, String> lru = new LinkedHashMap<Integer, String>() { @Override protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest) { return size() > cacheSize; } };调用示例
测试代码
View Code
运行结果
View Code
注:此文章属懒惰的肥兔原创,版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接
若您以为这篇文章还不错请点击下右下角的推荐,有了您的支持才能激发做者更大的写做热情,很是感谢。
若有问题,能够经过lzrabbit@126.com联系我。