理解设计模式——单例模式

在这里插入图片描述

定义

    确保一个类只有一个实例,并提供一个全局唯一访问点,用以访问该实例。

模式类型

    创建型设计模式

调用方式

在这里插入图片描述
    单例模式的设计结构如上图,所有调用者可根据getInstance()方法,获得Single类的唯一实例,结构为多对一。
    在Java单例模式的实现,应该注意以下事项:

  1. 为限制外部随意实例化Single类,Single类的构造方法应设置为private对外隐藏。
  2. 保证Single类有唯一的实例,Single实例需要在类中保存,那么Single实例对象需设置为private的私有变量,同时在多线程环境下的也应获取到唯一的实例
  3. 为使其他类能够调用全局访问点获取唯一实例,那么getInstance()方法需要设置为public静态(类)方法。

实现方法

    要想实现单例模式很简单,但要想实现兼具性能,线程安全的单例模式则需要做一些改动,目前有以下多种单例模式的实现。

饿汉式线程安全

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

优点:线程安全
缺点:无法达到lazy loading(延迟加载),资源浪费

懒汉式非线程安全

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

    此方式的优点在于lazy loading(延迟加载)可节省资源,但是由于getInstance()方法访问未加锁, 会出现线程不安全的情况。
优点:lazy loading
缺点:线程不安全

懒汉式线程安全

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

优点:lazy loading,线程安全
缺点:synchronized导致性能较低

双重校验锁DCL

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}

优点:lazy loading,线程安全,高性能
缺点:实现较复杂
    迷思小贴士:

     双重效验锁为什么要在单例上加"volatile"关键词呢?
     主要是为了禁止指令重排序,java中初始化一个实例(A a = new A())在java字节码中基本会有以下4个步骤:
        1.申请内存空间
        2.初始化默认值
        3.执行构造器方法连接引用和实例
        4.连接引用和实例
    这4个步骤中会进行指令重排,1234 、1243都有可能,有可能造成未初始化完全的对象发布,导致其他线程访问到未初始化的对象,加入volatile可确保Java线程内存模型所有线程看到这个变量的值是一致的,同时还会禁止指令重排序。

登记式

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

优点:lazy loading,线程安全,高性能,实现简单
缺点:第一次加载时反应较慢,存在反序列化攻击或反射攻击

枚举

public enum Singleton {  
    INSTANCE;  
    public Singleton getInstance() { 
      return INSTANCE; 
    }  
}

优点:lazy loading,线程安全,高性能,实现简单,自动支持序列化机制
缺点:未广泛使用
    迷思小贴士:

     根据effective java书中的建议,单例模式的最佳体验就是采用枚举模式,写法简单,利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题,枚举不存在反序列化问题,并且也能避免反射攻击。

使用场景

  • 创建某个对象需要消耗过多的资源以及时间,则需要控制实例产生数量,达到节约资源的目的
  • 频繁创建、销毁的对象
  • 整个系统只允许存在一个对象,例如上下文环境、资源管理器、回收站等
  • 一个实例的内部状态能被多个调用者共享时,避免多次创建性能损耗,例如数据库连接、日志文件、应用配置
  • 方便资源相互通信,进行资源同步的环境,例如累加器,广播变量等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值