共计 17497 个字符,预计需要花费 44 分钟才能阅读完成。
本地 Cache 零碎繁难设计
为什么应用缓存?
- 升高数据库的拜访压力。
- 进步查问效率。
- 改善用户体验。
你都理解哪些缓存?
- 数据库内置缓存(DBA 批改)。
- 数据层缓存(由长久层框架决定, 例如 mybatis)
- 业务层缓存(由业务层框架以及第三缓存产品决定: 本地缓存 + 分布式缓存)
- 浏览器缓存(Cache-Control)
设计缓存都应该思考什么问题?
- 存储构造:应用什么构造存储数据?(数组,链表,散列存储 - 哈希存储)
- 淘汰算法:无限容量(LRU,FIFO,…..),不限容量(GC)
- 并发平安:保障线程平安。
- 任务调度:每隔多长时间清理一下缓存。
- 日志记录:是否命中?(命中率)
缓存零碎设计根底
缓存规范定义
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();} |
繁难 Cache 实现
场景利用:
- 存储数据量比拟小(因为没有思考淘汰机制)
- 没有线程共享(一个线程的外部缓存)
- 缓存对象生命周期比拟短
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()); | |
} | |
} |
构建线程平安 Cache 对象
场景利用:并发环境
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 有什么劣势,劣势?
反对日志记录的 Cache 实现
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 记录日志的形式有什么不好的中央?(信息的完整性,同步问题)
LruCache 实现
利用场景:基于 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); | |
} | |
} |
设置 Cache 淘汰算法:FIFO 算法
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 的实现
场景:存储到 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); | |
} | |
} |
软件援用 Cache 实现
利用场景:内存不足时淘汰缓存中数据
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); | |
} | |
} |
弱 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();} | |
} | |
} |
异步日志 Cache 实现
第一步:增加依赖
` <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"); | |
} | |
} |
Kryo 构建序列化 Cache
第一步:增加依赖
<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();} | |
} |
> 构建高性能序列化 Cache
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 | |
} | |
} |
正文完