package com.cy.java.cache; /\*\* Cache接口設計\*/ public interface Cache { public void putObject(Object key,Object value); public Object getObject(Object key); public Object removeObject(Object key); public void clear(); public int size(); }
場景應用:
package com.cy.java.cache; import java.util.HashMap; import java.util.Map; /**負責真正存儲數據的一個對象,將數據存儲到一個map中*/ public class PerpetualCache implements Cache { /** 特色:線程不安全,key不容許重複,不能保證key的順序 */ private Map<Object,Object> cache=new HashMap<>(); @Override public void putObject(Object key, Object value) { cache.put(key, value); } @Override public Object getObject(Object key) { return cache.get(key); } @Override public Object removeObject(Object key) { return cache.remove(key); } @Override public void clear() { cache.clear(); } @Override public int size() { return cache.size(); } @Override public String toString() { return cache.toString(); } public static void main(String\[\] args) { Cache cache=new PerpetualCache(); cache.putObject("A", 100); cache.putObject("B", 200); cache.putObject("C", 300); System.out.println(cache); cache.removeObject("D"); cache.clear(); System.out.println(cache.size()); } }
場景應用:併發環境
package com.cy.java.cache; /**線程安全的cache對象*/ public class SynchronizedCache implements Cache{ private Cache cache; public SynchronizedCache(Cache cache) { this.cache=cache; } @Override public synchronized void putObject(Object key, Object value) { cache.putObject(key, value); } @Override public synchronized Object getObject(Object key) { // TODO Auto-generated method stub return cache.getObject(key); } @Override public synchronized Object removeObject(Object key) { // TODO Auto-generated method stub return cache.removeObject(key); } @Override public synchronized void clear() { cache.clear(); } @Override public synchronized int size() { return cache.size(); } @Override public String toString() { return cache.toString(); } public static void main(String\[\] args) { SynchronizedCache cache= new SynchronizedCache(new PerpetualCache()); cache.putObject("A", 100); cache.putObject("B", 200); cache.putObject("C", 300); System.out.println(cache); } }
思考:對於SynchronizedCache 有什麼優點,劣勢?java
package com.cy.java.cache; /** 用於記錄命中率的日誌cache*/ public class LoggingCache implements Cache { private Cache cache; /**記錄請求次數*/ private int requests; /**記錄命中次數*/ private int hits; public LoggingCache(Cache cache) { this.cache=cache; } @Override public void putObject(Object key, Object value) { cache.putObject(key, value); } @Override public Object getObject(Object key) { requests++; Object obj=cache.getObject(key); if(obj!=null)hits++; System.out.println("Cache hit Ratio : "+hits\*1.0/requests); return obj; } @Override public Object removeObject(Object key) { return cache.removeObject(key); } @Override public void clear() { cache.clear(); } @Override public int size() { return cache.size(); } @Override public String toString() { // TODO Auto-generated method stub return cache.toString(); } public static void main(String\[\] args) { SynchronizedCache cache= new SynchronizedCache( new LoggingCache( new PerpetualCache())); cache.putObject("A", 100); cache.putObject("B", 200); cache.putObject("C", 300); System.out.println(cache); cache.getObject("D"); cache.getObject("A"); } }
思考:你以爲LoggingCache記錄日誌的方式有什麼很差的地方?(信息的完整性,同步問題)算法
應用場景:基於LRU算法的的基本實現
package com.cy.java.cache; import java.util.LinkedHashMap; import java.util.Map; /** 緩存淘汰策略:LRU(最近最少使用算法)*/ public class LruCache implements Cache { private Cache cache; /**經過此屬性記錄要移除的數據對象*/ private Object eldestKey; /**經過此map記錄key的訪問順序*/ private Map<Object,Object> keyMap; @SuppressWarnings("serial") public LruCache(Cache cache,int maxCap) { this.cache=cache; //LinkedHashMap能夠記錄key的添加順序或者訪問順序 this.keyMap= new LinkedHashMap<Object,Object>(maxCap, 0.75f, true) {//accessOrder //此方法每次執行keyMap的put操做時調用 @Override protected boolean removeEldestEntry (java.util.Map.Entry<Object, Object> eldest) { boolean isFull=size()>maxCap; if(isFull)eldestKey=eldest.getKey(); return isFull; } }; } @Override public void putObject(Object key, Object value) { //存儲數據對象 cache.putObject(key, value); //記錄key的訪問順序,假如已經滿了,就要從cache中移除數據 keyMap.put(key, key);//此時會執行keyMap對象的removeEldestEntry if(eldestKey!=null) { cache.removeObject(eldestKey); eldestKey=null; } } @Override public Object getObject(Object key) { keyMap.get(key);//記錄key的訪問順序 return cache.getObject(key); } @Override public Object removeObject(Object key) { return cache.removeObject(key); } @Override public void clear() { cache.clear(); keyMap.clear(); } @Override public int size() { return cache.size(); } @Override public String toString() { return cache.toString(); } public static void main(String\[\] args) { SynchronizedCache cache= new SynchronizedCache( new LoggingCache( new LruCache(new PerpetualCache(),3))); cache.putObject("A", 100); cache.putObject("B", 200); cache.putObject("C", 300); cache.getObject("A"); cache.getObject("C"); cache.putObject("D", 400); cache.putObject("E", 500); System.out.println(cache); } }
package com.cy.java.cache; import java.util.Deque; import java.util.LinkedList; /** * FifoCache :基於FIFO算法(對象滿了要按先進先出算法移除對象)實現cache對象 */ public class FifoCache implements Cache{ /**藉助此對象存儲數據*/ private Cache cache; /**藉助此隊列記錄key的順序*/ private Deque<Object> keyOrders; /**經過此變量記錄cache能夠存儲的對象個數*/ private int maxCap; public FifoCache(Cache cache,int maxCap) { this.cache=cache; keyOrders=new LinkedList<>(); this.maxCap=maxCap; } @Override public void putObject(Object key, Object value) { //1.記錄key的順序(起始就是存儲key,添加在隊列最後位置) keyOrders.addLast(key); //2.檢測cache中數據是否已滿,滿了則移除。 if(keyOrders.size()>maxCap) { Object eldestKey=keyOrders.removeFirst(); cache.removeObject(eldestKey); } //3.放新的對象 cache.putObject(key, value); } @Override public Object getObject(Object key) { return cache.getObject(key); } @Override public Object removeObject(Object key) { Object obj=cache.removeObject(key); keyOrders.remove(key); return obj; } @Override public void clear() { cache.clear(); keyOrders.clear(); } @Override public int size() { return cache.size(); } @Override public String toString() { // TODO Auto-generated method stub return cache.toString(); } public static void main(String\[\] args) { Cache cache= new SynchronizedCache( new LoggingCache( new FifoCache( new PerpetualCache(),3))); cache.putObject("A",100); cache.putObject("B",200); cache.putObject("C",300); cache.getObject("A"); cache.putObject("D",400); cache.putObject("E",500); System.out.println(cache); } }
場景:存儲到cache的是對象的字節
package com.cy.java.cache; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializedCache implements Cache { private Cache cache; public SerializedCache(Cache cache) { this.cache=cache; } /**序列化*/ private byte\[\] serialize(Object value) { //1.構建流對象 ByteArrayOutputStream bos=null; ObjectOutputStream oos=null; try { //1.2構建字節數組輸出流,此流對象內置可擴容的數組。 bos=new ByteArrayOutputStream(); //1.3構建對象輸出流 oos=new ObjectOutputStream(bos); //2.對象序列化 oos.writeObject(value); //此時對象會以字節的方式寫入到字節數組輸出流 oos.flush(); return bos.toByteArray(); }catch (Exception e) { throw new RuntimeException(e); }finally { //3.關閉流對象 if(bos!=null) try{bos.close();bos=null;}catch(Exception e) {} if(oos!=null) try{oos.close();oos=null;}catch (Exception e2) {} } } /**反序列化*/ public Object deserialize(byte\[\] value) { //1.建立流對象 ByteArrayInputStream bis=null; ObjectInputStream ois=null; try { //1.1構建字節數組輸入流,此對象能夠直接讀取數組中的字節信息 bis=new ByteArrayInputStream(value); //1.2構建對象輸入流(對象反序列化) ois=new ObjectInputStream(bis); //2.反序列化對象 Object obj=ois.readObject(); return obj; }catch(Exception e) { throw new RuntimeException(e); }finally { //3.關閉流對象 if(bis!=null) try{bis.close();bis=null;}catch(Exception e) {} if(ois!=null) try{ois.close();ois=null;}catch (Exception e2) {} } } @Override public void putObject(Object key, Object value) { cache.putObject(key, serialize(value)); } @Override public Object getObject(Object key) { return deserialize((byte\[\])cache.getObject(key)); } @Override public Object removeObject(Object key) { return cache.removeObject(key); } @Override public void clear() { cache.clear(); } @Override public int size() { return cache.size(); } public static void main(String\[\] args) { Cache cache=new SerializedCache(new PerpetualCache()); cache.putObject("A", 200); cache.putObject("B", 300); Object v1=cache.getObject("A"); Object v2=cache.getObject("A"); System.out.println(v1==v2); System.out.println(v1); System.out.println(v2); } }
應用場景:內存不足時淘汰緩存中數據
package com.cy.java.cache; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; /**軟引用*/ public class SoftCache implements Cache { private Cache cache; private ReferenceQueue<Object> garbageOfRequenceQueue= new ReferenceQueue<>(); public SoftCache(Cache cache) { this.cache=cache; } @Override public void putObject(Object key, Object value) { //1.移除一些垃圾對象(Soft引用引用的已經被回收的對象) removeGarbageObjects(); //2.將對象存儲到cache(key不變,Value爲爲soft引用對象) cache.putObject(key, new SoftEntry(key, value, garbageOfRequenceQueue)); } @Override public Object getObject(Object key) { //1.基於key獲取軟引用對象並判斷 SoftEntry softEntry=(SoftEntry)cache.getObject(key); if(softEntry==null)return null; //2.基於軟引用對象獲取它引用的對象並判斷 Object target = softEntry.get(); if(target==null)cache.removeObject(key); return target; } @Override public Object removeObject(Object key) { //1.移除一些垃圾對象(Soft引用引用的已經被回收的對象) removeGarbageObjects(); //2.從cache中移除對象 Object removedObj=cache.removeObject(key); return removedObj; } @Override public void clear() { //1.移除一些垃圾對象(Soft引用引用的已經被回收的對象) removeGarbageObjects(); //2.清空cache cache.clear(); } @Override public int size() { removeGarbageObjects(); return cache.size(); } private void removeGarbageObjects() { SoftEntry softEntry=null; //1.從引用隊列中獲取已經被GC的一些對象的引用 while((softEntry= (SoftEntry)garbageOfRequenceQueue.poll())!=null){ //softEntry不爲null表示softEntry引用的對象已經被移除 //2.從cache中將對象引用移除。 cache.removeObject(softEntry.key); } } /\*\*定義軟引用類型\*/ private static class SoftEntry extends SoftReference<Object\>{ private final Object key; public SoftEntry(Object key, Object referent, ReferenceQueue<? super Object> rQueue) { super(referent, rQueue); this.key=key; } } @Override public String toString() { // TODO Auto-generated method stub return cache.toString(); } public static void main(String\[\] args) { Cache cache=new SoftCache(new PerpetualCache()); cache.putObject("A", new byte\[1024\*1024\]); cache.putObject("B", new byte\[1024\*1024\]); cache.putObject("C", new byte\[1024\*1024\]); cache.putObject("D", new byte\[1024\*1024\]); cache.putObject("E", new byte\[1024\*1024\]); System.out.println(cache.size()); System.out.println(cache); } }
應用場景:GC觸發清除緩存對象
package com.cy.java.cache; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; /\*\*弱引用\*/ public class WeakCache implements Cache { private Cache cache; private ReferenceQueue<Object> garbageOfRequenceQueue= new ReferenceQueue<>(); public WeakCache(Cache cache) { this.cache=cache; } @Override public void putObject(Object key, Object value) { //1.移除一些垃圾對象(Soft引用引用的已經被回收的對象) removeGarbageObjects(); //2.將對象存儲到cache(key不變,Value爲爲soft引用對象) cache.putObject(key, new WeakEntry(key, value, garbageOfRequenceQueue)); } @Override public Object getObject(Object key) { //1.基於key獲取軟引用對象並判斷 WeakEntry softEntry=(WeakEntry)cache.getObject(key); if(softEntry==null)return null; //2.基於軟引用對象獲取它引用的對象並判斷 Object target = softEntry.get(); if(target==null)cache.removeObject(key); return target; } @Override public Object removeObject(Object key) { //1.移除一些垃圾對象(Soft引用引用的已經被回收的對象) removeGarbageObjects(); //2.從cache中移除對象 Object removedObj=cache.removeObject(key); return removedObj; } @Override public void clear() { //1.移除一些垃圾對象(Soft引用引用的已經被回收的對象) removeGarbageObjects(); //2.清空cache cache.clear(); } @Override public int size() { removeGarbageObjects(); return cache.size(); } private void removeGarbageObjects() { WeakEntry softEntry=null; //1.從引用隊列中獲取已經被GC的一些對象的引用 while((softEntry= (WeakEntry)garbageOfRequenceQueue.poll())!=null) { //softEntry不爲null表示softEntry引用的對象已經被移除 //2.從cache中將對象引用移除。 cache.removeObject(softEntry.key); } } /**定義軟引用類型*/ private static class WeakEntry extends WeakReference<Object\>{ private final Object key; public WeakEntry(Object key, Object referent, ReferenceQueue<? super Object> rQueue) { super(referent, rQueue); this.key=key; } } @Override public String toString() { return cache.toString(); } public static void main(String\[\] args) { Cache cache=new WeakCache(new PerpetualCache()); cache.putObject("A", new byte\[1024\*1024\]); cache.putObject("B", new byte\[1024\*1024\]); cache.putObject("C", new byte\[1024\*1024\]); cache.putObject("D", new byte\[1024\*1024\]); cache.putObject("E", new byte\[1024\*1024\]); cache.putObject("F", new byte\[1024\*1024\]); cache.putObject("G", new byte\[1024\*1024\]); System.out.println(cache.size()); System.out.println(cache); } }
package com.cy.java.cache; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 構建線程安全對象,基於ReentrantReadWriteLock對象實現讀寫鎖應用。 * @author qilei */ public class ReentrantLockCache implements Cache { private Cache cache; /** * 此對象提供了讀鎖,寫鎖應用方式. * 1)寫鎖:排他鎖 * 2)讀鎖:共享鎖 * 說明:讀寫不能同時執行。 */ private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public ReentrantLockCache(Cache cache) { this.cache=cache; // TODO Auto-generated constructor stub } @Override public void putObject(Object key, Object value) { readWriteLock.writeLock().lock(); try { cache.putObject(key, value); }finally { readWriteLock.writeLock().unlock(); } } @Override public Object getObject(Object key) { readWriteLock.readLock().lock(); try { Object object=cache.getObject(key); return object; }finally{ readWriteLock.readLock().unlock(); } } @Override public Object removeObject(Object key) { readWriteLock.writeLock().lock(); try { Object object=cache.removeObject(key); return object; }finally{ readWriteLock.writeLock().unlock(); } } @Override public void clear() { readWriteLock.writeLock().lock(); try { cache.clear(); }finally{ readWriteLock.writeLock().unlock(); } } @Override public int size() { readWriteLock.readLock().lock(); try { int size=cache.size(); return size; }finally{ readWriteLock.readLock().unlock(); } } }
第一步:添加依賴
` <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> `
第二步:添加配置文件logback.xml (參考項目代碼)
<?xml version="1.0" encoding="UTF-8"?> <configuration> <logger name="com.cy" level="TRACE" /> <appender name="FILE" class\="ch.qos.logback.core.rolling.RollingFileAppender"\> <rollingPolicy class\="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"\> <!--文件路徑,定義了日誌的切分方式----把每一天的日誌歸檔到一個文件中,以防止日誌填滿整個磁盤空間 --> <fileNamePattern>logs/context-log.%d{yyyy-MM-dd}.log </fileNamePattern> <!--只保留最近30天的日誌 --> <maxHistory>30</maxHistory> </rollingPolicy> <encoder charset="UTF-8"\> <pattern>\[%-5level\] %date --%thread-- \[%logger\] %msg %n</pattern> </encoder> </appender> <appender name="ASYNC\_FILE" class\="ch.qos.logback.classic.AsyncAppender"\> <discardingThreshold>0</discardingThreshold> <queueSize>256</queueSize> <appender-ref ref="FILE" /> </appender> <root level="debug"\> <appender-ref ref="ASYNC\_FILE" /> </root> </configuration>
第三步:構建AsyncLoggingCache
package com.cy.java.cache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 經過此對象異步記錄查詢操做的命中率 * 1)選擇日誌庫 * 2)執行異步寫操做。 */ public class AsyncLoggingCache implements Cache { //日誌門面應用 private static Logger log=LoggerFactory.getLogger(LoggingCache.class); private Cache cache; /**表示請求次數*/ private int requests; /**命中次數(命中表示從緩存中取到數據了)*/ private int hits; public AsyncLoggingCache(Cache cache) { this.cache=cache; } @Override public void putObject(Object key, Object value) { cache.putObject(key, value); } @Override public Object getObject(Object key) { requests++; Object obj=cache.getObject(key); if(obj!=null)hits++; //記錄日誌耗時 log.info("Cache hit Ratio:{}",hits\*1.0/requests); return obj; } @Override public Object removeObject(Object key) { return cache.removeObject(key); } @Override public void clear() { cache.clear(); } @Override public int size() { return cache.size(); } public static void main(String\[\] args) { Cache cache= new AsyncLoggingCache(new PerpetualCache()); cache.putObject("A", 100); cache.putObject("B", 200); cache.putObject("C", 300); cache.putObject("D", 400); //System.out.println(cache); cache.getObject("E"); cache.getObject("A"); cache.getObject("B"); } }
第一步:添加依賴
<dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>5.0.0-RC5</version> </dependency>
第二步:構建項目工具類
public class KryoUtils { /** * 多線程併發執行時,可能會出現線程不安全,具體緣由是什麼? * 1)多個線程的併發 * 2)多個線程有數據共享 * 3)多個線程在共享數據集上的操做不是原子操做 * * 分析:當出現了線程不安全,如何進行修改來保證線程安全 * 1)將多線程改成單線程。 * 2)取消共享 (例如在當前應用中咱們一個線程一個Kryo對象) * 3)加鎖+CAS * * ThreadLocal提供了這樣的一種機制: * 1)能夠將對象綁定到當前線程(實際上是將對象存儲到當前線程的map中) * 2)能夠從當前線程獲取綁定的對象(從當前線程的map中獲取對象) */ static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() { protected Kryo initialValue() { Kryo kryo = new Kryo(); // Configure the Kryo instance. kryo.setRegistrationRequired(false); //.... return kryo; }; }; public static Object deserialize(byte\[\] array){ Kryo kryo=kryos.get(); Input input = new Input(new ByteArrayInputStream(array)); Object obj=kryo.readClassAndObject(input); return obj; } public static byte\[\] serialize(Object object){ //從當前線程獲取kryo對象,當前線程沒有會調用ThreadLocal的initialValue方法建立對象並綁定線程 Kryo kryo=kryos.get(); ByteArrayOutputStream bos=new ByteArrayOutputStream(); Output output = new Output(bos); kryo.writeClassAndObject(output, object); output.close(); return bos.toByteArray(); } }
public class KryoSerializedCache implements Cache { private Cache cache; public KryoSerializedCache(Cache cache) { this.cache=cache; } @Override public void putObject(Object key, Object value) { //1.將對象序列化 byte\[\] array=KryoUtils.serialize(value); //2.將序列化後的字節數組引用存儲到cache cache.putObject(key,array); } @Override public Object getObject(Object key) { //1.基於key獲取緩存中的字節數組引用 byte\[\] array=(byte\[\])cache.getObject(key); //2.將字節數組反序列化爲對象 return KryoUtils.deserialize(array); } @Override public Object removeObject(Object key) { return KryoUtils.deserialize((byte\[\])cache.removeObject(key)); } @Override public void clear() { cache.clear(); } @Override public int size() { return cache.size(); } public static void main(String\[\] args) { Cache cache=new KryoSerializedCache(new PerpetualCache()); cache.putObject("A", 500); Object a1=cache.getObject("A"); Object a2=cache.getObject("A"); System.out.println("a1="+a1); System.out.println("a2="+a2); System.out.println(a1==a2);//false } }