码农在囧途
人的挫败感大多来自努力了很久,没有看到成效,从而心里产生很大的落差,就像喜欢一个女孩子很久,一直聊得很开,并且彼此的反馈都很不错, 终于有一天鼓起勇气说出来了,但是她却说,我只是把你当成好朋友来看待,其实女孩子说的可能是她的真实想法,但是我觉得大多的原因应该是 时间拖得太久了,以至于本来对方的那种激烈的内心已经平复下来了,这就是不勇敢而导致的,其实不论是追女孩子,还是学习,工作,都要学会 看时机,有些东西错过了真的会后悔的,你五年前在学校想上台去表演一下,但是由于你自卑,不自信,所以错过了一次体验,一些美好的回忆, 但是今天你回忆起来,你会觉得,即使上去丢脸了,也没啥啊,老子今天依然还活着啊!同样,今天,你想做的事如果你不去做,那么五年后, 你也会后悔,可是,人生又有多少个五年呢!
享元模式定义
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率, 我们平时用到的线程池,连接池都是类似的思想,享元模式属于结构型设计模式。
应用示例
我们知道像微博这种流量很大的系统,在某明星进行官宣,或者塌房的时候,微博的搜索量总是很大,像前段时间王先生,还有凡凡,都是这种情况,对于 这种爆炸消息,消息肯定不能存放在关系型数据库中,要么存放在缓存中,比如Redis
,要么存在搜索引擎中,比如elasticsearch
,其目的都是减少 数据库的操作和搜索对象的创建,今天我们用享元模式来实现热点数据的查询,提升性能,减少内存的消耗。
代码结构
代码语言:javascript复制com
└─steak
└─desgin
└─flyweight
├─HotspotData.java
└─HotSpotDataClient.java
└─HotspotDataFactory.java
└─HotSpotRpcService.java
└─IHotspotData.java
└─R.java
编码
1.定义热点数据接口IHotspotData
此接口定义了获取热点数据的方法getHotSpotData
,参数为key,比如搜索凡凡
。
public interface IHotspotData {
R getHotSpotData(String key);
}
2.热点数据接口实现类HotspotData
实现类主要是调用热点数据Rpc接口获取热点数据。
代码语言:javascript复制public class HotspotData implements IHotspotData {
@Override
public R getHotSpotData(String key){
HotSpotRpcService hotSpotRpcService = new HotSpotRpcService();
return hotSpotRpcService.queryHotSpotDataByKey(key);
}
}
3.模拟获取热点数据Rpc接口
此接口模仿获取数据库中的数据,进行一定的休眠,标识请求消耗的时间。
代码语言:javascript复制public class HotSpotRpcService {
public R queryHotSpotDataByKey(String key) throws InterruptedException {
//模拟获取数据时间
TimeUnit.MILLISECONDS.sleep(10);
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("title","王思聪当舔狗");
hashMap.put("content","王思聪和孙一宁的聊天中出现了舔狗情景,“宝,今天输液,输的什么液,想你的夜”");
return new R(200,"获取成功",hashMap);
}
}
4.热点数据享元工厂
热点数据享元工厂里面主要进行对象实例和数据的缓存,其结构是HashMap,getHotspotDataByKey
方法中如果发现热点key存在 缓存中,那么就取缓存中的数据,如果缓存中不存在,就获取数据后将数据存储在缓存中,以便别人获取直接从缓存中获取,防止创建大量的对象(HotspotData)和频繁的 访问数据接口。
public class HotspotDataFactory {
private static final Map<String, R> hotMap = new HashMap<>();
public static R getHotspotDataByKey(String key) throws InterruptedException {
if (!hotMap.containsKey(key)) {
IHotspotData instance = new HotspotData();
R data = instance.getHotSpotData(key);
hotMap.put(key, data);
}
return hotMap.get(key);
}
}
5.统一返回结构体
代码语言:javascript复制@Data
@AllArgsConstructor
public class R {
private int code;
private String msg;
private Object data;
}
6.代码测试
在测试代码中,我们发送10000个请求取获取数据,并且记录下所消耗的时间。
代码语言:javascript复制public class HotSpotDataClient {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
String key = "王思聪当舔狗";
for (int i = 0 ; i < 10000 ; i ){
R hotspotData = HotspotDataFactory.getHotspotDataByKey(key);
System.out.println(hotspotData);
}
long time = System.currentTimeMillis() - start;
System.out.println("花费时间: " time);
}
}
从返回结果中看出消耗了84毫秒。
以上是我们使用了享元模式来对数据进行了缓存,所以要的时间非常短,那么我们下面来测试不使用享元模式的情况下,需要消耗多少时间呢。
7.不使用享元模式
没有使用享元模式,其实就是不使用缓存了,每次获取数据需要先创建一个对象实例,然后再去调用方法。
代码语言:javascript复制public class HotspotDataFactory {
public static R getHotspotDataByKey(String key) throws InterruptedException {
return new HotspotData().getHotSpotData(key);
}
}
从图看出不使用享元模式的话,每次都要创建对象,然后再去调用获取热点数据的接口,大量的时间都花费再创建对象和远程Rpc接口上面,所以花费 了大量的时间,因为像这种数据本身就不会发生改变,所以就直接存在缓存中就行了,每次去数据库中查询是不理智的。
UML图
结语
关于享元模式的介绍就到这里,通过享元模式,我们可以减少对对象的创建,从而减少内存的开销,因为如果在某一时刻,创建了大量的对象, 那么是有可能导致OOM的,而享元模式的思想就能很好的避免这个问题,它的核心理念就是缓存,通过缓存来提高对象和数据的复用, 不过现在如果涉及到频繁的数据,我们可以存放在缓存中间件中,但是对于对象的创建,享元模式依然值得我们借鉴。
今天的分享就到这里,感谢你的观看,我们下期见。