设计模式学习笔记之单例模式

 

什么是单例模式? 

顾名思义,就是只有一个实例,也就是说一个应用程序中只有一个实例对象.

既然一个系统,一个应用程序中只有一个实例,那么也就说明了如果操作这一个对象,必然涉及到共享资源,涉及到资源分配问题,资源竞争等问题. 

那么我们的应用场景是什么呢?

1. 网站的在线人数. 网站的在线人数在某一个时刻,所有人看到的是一样的, 是这个网站这个时刻,所有用户所共享的.

2. 池化技术. 比如数据库的连接池. 每个数据库的可支持的连接数量是有限,而且连接对象创建和销毁也是比较耗内存的. 通过一个统一的入口去控制,可以保证对数据库的压力在可控的范围内,同是也可以保证出具库连接的持续使用.

3. 配置中心. 一个应用程序针对通过一个配置文件的加载只需要加载一次即可,不需要多次加载.

 

其实以上只是一些常见的应用场景,当然单例模式的应用场景也远不止于此.

本文参考的博客地址为:https://www.cnblogs.com/mr-yang-localhost/p/9644417.html

参考的博客的写的很不错, 如果想了解更多,可以去看看这篇参考的博客

接下来我们来看一下常见的实现方式,以及其中的对比.

 饿汉式,即直接初始化好,使用的时候直接调用即可.

package com.cbl.design.singletondesign;

public class HungrySingleton {
    private static final HungrySingleton singleton = new HungrySingleton();

    //限制外部产生HungrySingleton对象
    private HungrySingleton(){ }

    //向外提供获取示例的静态方法
    public static HungrySingleton getInstance() {
        return singleton;
    }


}

  

懒汉式, 顾名思义,就是懒,需要的时候再去初始化

package com.cbl.design.singletondesign;

public class LazySingleton {
    private static volatile LazySingleton singleton = null;

    private LazySingleton() { }

    public static LazySingleton getSingleton() {
        //不用每次获取对象都强制加锁,为了提升了效率
        if (singleton == null) {
            synchronized (LazySingleton.class) {
                if (singleton == null) {
                    singleton = new LazySingleton();
                }
            }
        }
        return singleton;
    }
}

  

静态内部类的方式

 

package com.cbl.design.singletondesign;

public class StaticInnerSingleton {
    /**
     * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     * 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
     */
    private static class Singleton2Holder {
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static StaticInnerSingleton singleton = new StaticInnerSingleton();
    }

    private StaticInnerSingleton() {
        //System.out.println("singleton2 private construct method called");
    }

    public static StaticInnerSingleton getSingleton() {
        //System.out.println("singleton2 getSingleton method called");
        return Singleton2Holder.singleton;
    }

    private String name;

    public void desc() {
        //System.out.println("singleton2 desc method called");
    }
}

  

还有一种实现方式是依赖于枚举, 我们知道枚举中的对象是实例化好的,而且枚举天生要求枚举类的构造器必须私有,而且本身还是被final 修饰的, 不可被继承

package com.cbl.design.singletondesign;

public enum EnumSingleton {
    INSTANCE;

    public String getDesc() {
        return "desc";
    }

    public static void process() {
        System.out.println("static process method");
    }

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

  

以上是4中常见的构造单例模式的四种线程安全的构造单例的设计模式.网上可能还有其余的非线程安全的构建单例的方法, 但是线程安全的主要就是这四种.

(1) : 饿汉式 :  加载类的时候就会进行实例化,,如果后续没有用到,则会搞成资源浪费.

(2) : 懒汉式 : 使用的时候再去初始化,这样在前几次调用并发调用的时候,会出现资源竞争, 等待时间较久,但是后续再次调用的时候, 由于是双重判断加加锁校验机制, 不会出现阻塞.

(3): 静态内部类 : 也是在使用的时候再去加载内部类并初始化外部类的对象,第一次调用会比较慢.

(4):枚举单例:  类加载的时候就穿创建好对象了, 特点和饿汉式很像.

 

上面是这几种单例方式的特点,其中懒汉式,饿汉式,静态内部类的方式都不能防止反射,只有枚举类可以防止反射.

但是一般我们的应用程序只需要保证可以正常实现单例即可,没必要过多的去防止单例模式,我们使用单例模式,就是想构造单例对象的,但是我们自己又去使用反射去破坏单例模式,这不是自己给自己找麻烦吗.

 

posted on 2019-12-08 23:12  城序猿  阅读(175)  评论(0编辑  收藏  举报