1:享元模式概念
享元模式:主要通过对象的复用来减少对象创建的次数和数量,以减少系统内存的使用和降低系统的负载。享元模式属于结构型模式,在系统需要一个对象时享元模式首先在系统中查找并尝试重用现有的对象,如果未找到匹配的对象,则创建新对象并将其缓存在系统中以便下次使用。享元模式主要用于避免在有大量对象时频繁创建和销毁对象造成系统资源的浪费,把其中共同的部分抽象出来,如果有相同的业务请求,则直接返回内存中已有的对象,避免重新创建。
简单一点的说法:享元模式就是实现“对象池”,让对象共享使用,避免内存中频繁创建对象涉及到的各种开销。
在项目中出现很多相同或类似的对象,享元模式会创建一个享元池将这些公共的实例保存在享元池中。可以针对的创建不同的对象,然后通过复用的方式进行分配。需要的时候就将对应的对象取出,不需要则放回。享元模式能够实现共享技术!
2:实例说明
下面以内存的申请和使用为例介绍享元模式的使用方法,创建一个MemoryFactory作为内存管理的工厂,用户通过工厂获取内存,在系统内存池有可用内存时直接获取该内存,如果没有则创建一个内存对象放入内存池,等下次有相同的内存请求过来时直接将该内存分配给用户即可。
3:定义内存对象Memory:
/**
* 定义内存块
*
* @Author: ganbo
* @Date: 2020/6/12 12:30
*/
@Data
@AllArgsConstructor
public class Memory {
private String id; //内存id
private int size; //内存大小,单位MB
private boolean isUsed; //内存是否使用
}
4:定义内存“对象池”:
/**
* 内存池/内存工厂
*
* @Author: ganbo
* @Date: 2020/6/12 12:32
*/
public class MemoryPool {
private static List<Memory> memories = new ArrayList<Memory>();
/**
* 获取指定大小的内存
*
* @param size 需要的内存大小
*/
public static Memory getMemmory(int size) {
Memory memory = null;
for (int i = 0; i < memories.size(); i++) {
memory = memories.get(i);
//付过缓存中存在和需求大小size相同并且未使用过的内存块,就直接返回
if (memory.getSize() == size && !memory.isUsed()) {
memory.setUsed(true);
memories.set(i, memory);
System.out.println("获取内存->来至于缓存。");
break;
}
}
//如果缓存中的内存不存在,则从系统中新申请内存返回,并将该内存加入内存对象列表中管理
if (memory == null) {
memory = new Memory(UUID.randomUUID().toString(), size, true);
System.out.println("获取内存->来至于系统新的开销。");
memories.add(memory);
}
return memory;
}
/**
* 释放内存
*
* @param id 要释放的内存id
*/
public static void releaseMemory(String id) {
for (int i = 0; i < memories.size(); i++) {
Memory memory = memories.get(i);
if (memory.getId().equals(id)) {
memory.setUsed(false);
memories.set(i, memory);
System.out.println("释放内存,id:" + id);
break;
}
}
}
}
以上代码定义了工厂类MemoryPool,在该类中定义了memorys用于存储从系统中申请到的内存,该类定义了getMemory,用于从memorys列表中获取内存,如果在内存中有空闲的内存,则直接取出来返回,并将该内存的使用状态设置为已使用,如果没有,则创建内存并放入内存列表;还定义了releaseMemory来释放内存,具体做法是将内存的使用状态设置为false。
5:使用享元模式
public class MemoryPoolTest {
public static void main(String[] args) {
//首次获取内存,将创建一个内存
Memory memmory = MemoryPool.getMemmory(32);
//在使用过后释放内存
MemoryPool.releaseMemory(memmory.getId());
//重新获取内存,将从对象池缓存中获取
memmory = MemoryPool.getMemmory(32);
}
}
在使用享元模式时,直接从工厂类MemoryPool中获取需要的数据Memory,在使用完成后释放即可,具体的运行结果如下:
6:享有模式的优缺点及使用场景
优点:
- 节约系统的开销。保证一个常用的对象只有一个!
- 外部状态不会影响内部状态,可以在不同环境下进行共享。
缺点:
- 享元模式使逻辑变得更加复杂,需要将享元对象分出内部状态和外部状态。
- 新增了多余的类(pool),类膨胀?
使用场景:
- 一个系统有大量细粒度化的对象,占据大量的内存
- 对象大部分属性可以外部化,并且能将外部的属性放入内部属性中来。
- 使用享元模式需要维护享元池,所以要用那种常用的经常调用的对象可以使用享元模式。
源码和实战:
- 平时在写代码中往往为了避免多次查询数据库,会设计一个Map来缓存对象,从一个方法传递到另外一个方法中,第二个方法就不用再次查询了,比如做过的宜农特商城里面的下单接口,里面会提前查询商品,店铺,等信息。
- Jdk自带的类Integer中内部类IntegerCache使用缓存(-128~127范围),就是享元模式。
- 平时我们用的数据库连接池也是享元模式的体现(做过的Im聊天中的“账号池”解决只有100个免费账号)。