创建型设计模模式---单例模式

设计模式

序号内容链接地址
1设计模式七大原则https://blog.youkuaiyun.com/qq_39668819/article/details/115390615
2创建型设计模式–工厂模式https://blog.youkuaiyun.com/qq_39668819/article/details/115375928
3创建型设计模式–抽象工厂模式https://blog.youkuaiyun.com/qq_39668819/article/details/115390992
4创建型设计模式–单例模式https://blog.youkuaiyun.com/qq_39668819/article/details/115396191
5创建型设计模式–建造者模式https://blog.youkuaiyun.com/qq_39668819/article/details/115396212
6创建型设计模式—原型模式https://blog.youkuaiyun.com/qq_39668819/article/details/115396227
7结构型设计模式—代理模式https://blog.youkuaiyun.com/qq_39668819/article/details/115480346
8结构型设计模式—适配器模式https://blog.youkuaiyun.com/qq_39668819/article/details/115499090
9结构型设计模式—桥接模式https://blog.youkuaiyun.com/qq_39668819/article/details/115560823
10结构型设计模式—装饰模式https://blog.youkuaiyun.com/qq_39668819/article/details/115582291
11结构型设计模式—外观模式https://blog.youkuaiyun.com/qq_39668819/article/details/115643900
12结构型设计模式—享元模式https://blog.youkuaiyun.com/qq_39668819/article/details/115680930
13结构型设计模式—组合模式https://blog.youkuaiyun.com/qq_39668819/article/details/115720713
14行为型设计模式—模板方法模式https://blog.youkuaiyun.com/qq_39668819/article/details/115774426
15行为型设计模式—策略模式https://blog.youkuaiyun.com/qq_39668819/article/details/115804292
16行为型设计模式—命令模式https://blog.youkuaiyun.com/qq_39668819/article/details/115877361
17行为型设计模式—责任链模式https://blog.youkuaiyun.com/qq_39668819/article/details/115981287
18行为型设计模式—状态模式https://blog.youkuaiyun.com/qq_39668819/article/details/116077215
19行为型设计模式—观察者模式https://blog.youkuaiyun.com/qq_39668819/article/details/116141223
20行为型设计模式—中介者模式https://blog.youkuaiyun.com/qq_39668819/article/details/116177694
21行为型设计模式—迭代器模式https://blog.youkuaiyun.com/qq_39668819/article/details/116213033
22行为型设计模式—访问者模式https://blog.youkuaiyun.com/qq_39668819/article/details/116246907
23行为型设计模式—备忘录模式https://blog.youkuaiyun.com/qq_39668819/article/details/116333844
24行为型设计模式—解释器模式https://blog.youkuaiyun.com/qq_39668819/article/details/116379466
单例模式
定义与特点

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

单例模式在现实生活中的应用也非常广泛,例如公司 CEO、部门经理等都属于单例模型。J2EE 标准中的 Servlet Context 和 ServletContextConfig、Spring 框架应用中的 ApplicationContext、数据库中的连接池等也都是单例模式。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

单例模式有 3 个特点:

  • 单例类只有一个实例对象;

  • 该单例对象必须由单例类自行创建;

  • 单例类对外提供一个访问该单例的全局访问点。

这里主要介绍:饿汉式单例、懒汉式单例、静态内部类、枚举。

单例模式的结构

单例模式的主要角色如下:

  • 单例类:包含一个实例且能自行创建这个实例的类。
  • 访问类:使用单例的类
一、饿汉式单例
public class EagerSingleton {

    public static void main(String[] args) {
        Eager eager = Eager.getInstance();
        Eager eager2 = Eager.getInstance();

        System.out.println(eager);
        System.out.println(eager == eager2);
    }
}

/**
 * 饿汉式
 */
class Eager {

    private static final Eager eager = new Eager();

    private Eager() {

    }

    public static Eager getInstance() {
        return eager;
    }
}

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance() 方法之前单例已经存在了,是线程安全的

二、懒汉式单例
public class LazySingleton {

    public static void main(String[] args) {
        TwinCheck tc = TwinCheck.getInstance();
        TwinCheck tc2 = TwinCheck.getInstance();
        System.out.println(tc == tc2);
    }
}

/**
 * 三、双重校验
 */
class TwinCheck {

    /**
     * 注意: volatile 关键字不可省
     */
    private volatile static TwinCheck twinCheck= null;

    /**
     * 构造方法私有,避免在外部实例化对象
     */
    private TwinCheck() {

    }

    public static TwinCheck getInstance() {
        if (null == twinCheck) {
            synchronized (TwinCheck.class) {
                if (null == twinCheck) {
                    twinCheck = new TwinCheck();
                }
            }
        }
        return twinCheck;
    }
}

/**
 * 二、同步方法
 */
class SyncMethod {

    private static SyncMethod syncMethod = null;

    /**
     * 构造方法私有,避免在外部实例化对象
     */
    private SyncMethod() {

    }

    public static synchronized SyncMethod getInstance() {
        if (syncMethod == null) {
            syncMethod = new SyncMethod();
        }
        return syncMethod;
    }
}

/**
 * 一、非线程安全
 */
class Singleton {

    private static Singleton single = null;

    private Singleton() {
    }
    
    public static Singleton getInstance() {
        if (single == null) {
            single = new Singleton();
        }
        return single;
    }
}

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance() 方法时才去创建这个单例。

注意以上三种懒汉式单例中,建议试用第三种【双重校验】,第一种非线程安全,第二种效率很低【synchronized 方法】。

双重校验单例:线程安全、但是加入了 synchronized 同步块,对性能会有一定影响。

注意 volatile 是必不可少的,用于禁止指令重排:

由于 twinCheck = new TwinCheck() 不是一个原子性操作,将其步骤分为:

  • 1、分配内存空间
  • 2、执行构造方法,初始化对象
  • 3、把这个对象指向分配的内存空间

按我们的想法行顺序是:1->2->3,但指令重排后,执行顺序可能是 1->3->2【并不会影响执行结果】,单线程下这是完全没有问题的。

但是,当多线程的情况下就可能照成一个问题:

线程 A 通过 getInstance() 创建实例对象,这时由于指令重排,指令的执行顺序为 1->3->2,此时线程 A 执行完 3【将对象指向了内存空间,对象不再为 null】 还没有执行 2;线程 B 进来通过 getInstance() 方法获取对象,在判断 null == twinCheck 时,由于操作 3 已经执行完毕,twinCheck 对象不为空,返回 twinCheck 实例;但此时操作 2 还未执行,twinCheck 对象还没初始化,这时获取到的 twinCheck 对象就会存在问题。

所以需要加入 volatile 禁止指令重排,避免产生以上的问题。

三、静态内部类
/**
 * 静态内部类
 */
public class StaticInnerClass {

    private StaticInnerClass() {

    }

    private static class LazyModel {
        private static StaticInnerClass INSTANCE  = new StaticInnerClass();
    }

    public static StaticInnerClass getInstance() {
        return LazyModel.INSTANCE;
    }

    public static void main(String[] args) {
        StaticInnerClass sic = StaticInnerClass.getInstance();
        StaticInnerClass sic2 = StaticInnerClass.getInstance();
        System.out.println(sic == sic2);
    }
}

既实现了线程安全,又避免了同步带来的性能影响。

四、枚举
/**
 * 枚举
 */
public enum EnumSingleton {

    INSTANCE(0, "t");

    EnumSingleton(int code, String message) {
        this.code = code;
        this.msg = message;
    }

    private int code;

    private String msg;

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public static void main(String[] args) {
        EnumSingleton es = EnumSingleton.INSTANCE;
        EnumSingleton es2 = EnumSingleton.INSTANCE;
        System.out.println(es == es2);
    }
}

既实现了线程安全,又避免了同步带来的性能影响。比前两种更加安全,双重校验与静态内部类的单例可以通过反射破环,而枚举不能。

总结

单例模式的优点:

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

单例模式的缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值