GOF23 设计模式之享元模式

一、享元模式是什么?

享元模式(Flyweight Pattern):运用共享技术有效地支持大量细粒度对象的复用。系统只使用少量的对象,而这些对象都很相似,状态变化很小,可以实现对象的多次复用。由于 享元模式要求能够共享的对象必须是细粒度对象,因此它又称为轻量级模式,它是一种 对象结构型模式。

享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。

内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。

比如,连接池中的连接对象,保存在连接对象中的用户名、密码、连接URL等信息,在创建对象的时候就设置好了,不会随环境的改变而改变,这些为内部状态。而当每个连接要被回收利用时,我们需要将它标记为可用状态,这些为外部状态。

享元模式的本质是缓存共享对象,降低内存消耗。

二、结构

1、模式所涉及的角色

  • Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态;
  • ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
  • UnsharedConcreteFlyweight:
    非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象;
  • FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;

2、结构图

在这里插入图片描述
其中:

  1. UnsharedConcreteFlyweight 是非享元角色,里面包含了非共享的外部状态信息 info;
  2. Flyweight 是抽象享元角色,里面包含了享元方法 operation(UnsharedConcreteFlyweight
    state),非享元的外部状态以参数的形式通过该方法传入;
  3. ConcreteFlyweight 是具体享元角色,包含了关键字 key,它实现了抽象享元接口;
  4. FlyweightFactory 是享元工厂角色,它是关键字 key 来管理具体享元;
  5. 客户角色通过享元工厂获取具体享元,并访问具体享元的相关方法。

三、Java中的案例

String 常量池,线程池等;正好最近在看多线程,我们就拿多线程举例。

  1. 线程池中的大体结构

在这里插入图片描述

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;
        }
    }

  1. 对应的享元结构图为

在这里插入图片描述

简单的梳理

  1. DefaultThreadFactory 实现接口ThreadFactory,由newThread(Runnable run)返回一个Thread实例,DefaultThreadFactory 类是Exceutors的静态内部类
  2. ThreadPoolExecutor的 execute(Runnable command) 方法是执行任务的入口,并在此方法内调用addWorker(Runnable firstTask, boolean core)方法来增加全局的线程数和创建线程(核心线程和非核心线程,core参数用来区分,true为核心线程)
  3. 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()方法可以被激活。

总结

这里自己也是看了好久才有点理解,尤其是在把多线程的整个运行过程了解后。最后贴下运行过程图。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值