一、享元模式是什么?
享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于 享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种 对象结构型模式。
享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。
内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。
享元模式的本质是缓存共享对象,降低内存消耗。
二、结构
1、模式所涉及的角色
- Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态;
- ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
- UnsharedConcreteFlyweight:
非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象; - FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;
2、结构图
其中:
- UnsharedConcreteFlyweight 是非享元角色,里面包含了非共享的外部状态信息 info;
- Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight
state),非享元的外部状态以参数的形式通过该方法传入; - ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;
- FlyweightFactory 是享元工厂角色,它是关键字 key 来管理具体享元;
- 客户角色通过享元工厂获取具体享元,并访问具体享元的相关方法。
三、Java中的案例
String 常量池,线程池等;正好最近在看多线程,我们就拿多线程举例。
- 线程池中的大体结构
2.确定享元角色为Worker(下面是Worker类被简化的代码)
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
- 对应的享元结构图为
简单的梳理
- DefaultThreadFactory 实现接口ThreadFactory,由newThread(Runnable run)返回一个Thread实例,DefaultThreadFactory 类是Exceutors的静态内部类
- ThreadPoolExecutor的 execute(Runnable command) 方法是执行任务的入口,并在此方法内调用addWorker(Runnable firstTask, boolean core)方法来增加全局的线程数和创建线程(核心线程和非核心线程,core参数用来区分,true为核心线程)
- addWorker方法添加的线程就是Worker对象,Worker内部有两个重要的属性firstTask,thread,这里插一句Worker实现了Runnable接口,这很重要因为这意味着Worker内部有run()方法,贴下run()方法和Worker构造方法的代码
public void run() {
runWorker(this);
}
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
在addWorker方法中会调用上面的构造方法创建一个Worker实例,thread属性获取DefaultThreadFactory 返回的Thread实例,这样Worker内部有一个thread实例和你传进来的Runable实例。这时只要在addWorker方法中启动线程即t.start();那么Worker的run方法就会运行。这里要注意下构造方法中this.thread = getThreadFactory().newThread(this);这里已经把Worker对象传给了thread,所以run()方法可以被激活。
总结
这里自己也是看了好久才有点理解,尤其是在把多线程的整个运行过程了解后。最后贴下运行过程图。