二、01【设计模式】之单例模式

今天的博客主题

       设计模式 ——》 设计模式之单例模式


单例模式 SP (Singleton Pattern)

 

概念

单例模式是设计模式中最简单的模式。

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。

这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

 

单例模式三要素

1)单例类只能有一个实例。

2)单例类必须自己创建自己的唯一实例。

3)单例类必须给所有其他对象提供这一实例。

以此保证一个类仅有一个实例,并提供一个访问它的全局访问点。

主要解决一个全局使用的类频繁地创建与销毁。

优点

1)在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例

2)避免对资源的多重占用(比如写文件操作)

 

缺点

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化

 

使用场景

1)要求生产唯一序列号。

2)WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

3)创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

4)重量级对象,不需要多个实例,如线程池,数据库连接池

5)...

 

代码示例

单例模式有多种实现方式,不管哪种共同点就是:私有的构造函数提供全局访问点

饿汉式

特点:非懒加载,线程安全,实现简单,没有锁,效率高

缺点:产生垃圾对象,浪费内存

总结:比较常用,但容易产生垃圾对象。

类加载的初始化阶段就完成了实例的初始化。本质上是借助于 jvm classloader 机制,保证实例的唯一性(初始化过程只会执行一次)以及线程安全(JVM以同步的形式来完成类加载的过程)

public class Singleton {
    // 私有化构造函数
    private Singleton(){}
    // 类初始化时,就加载该对象。
    private static Singleton singleton= new Singleton();
    // 调用此方法,返回该对象
    public static Singleton getSingleton() {
        return singleton;
    }
}

懒汉式

特点:懒加载(延迟加载),实现简单

缺点:线程不安全,不支持多线程

总结:最基本的实现方式,最大的问题就是不支持多线程,懒加载很明显,线程不安全。没有加锁,严格意义上讲它不算是单例模式

public class Singleton {
    // 私有化构造函数
    private Singleton (){}
    // 声明该对象,但不会初始化,用到时候才会去实例化它
    private static Singleton singleton;
    // 调用此方法,返回该对象
    public static Singleton getSingleton() {
        // 对象为空,去实例化。不为空,直接返回
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉式(安全的)

特点:懒加载、线程安全,实现简单

缺点:加锁,效率低

总结:非常好的懒加载,第一次调用才会初始化,方法加锁,在多线程下可以很好的工作,效率低

public class Singleton {
    // 私有化构造函数
    private Singleton (){}
    // 声明该对象,但不会初始化,用到时候才会去实例化它
    private static Singleton singleton;
    // 方法加锁,调用此方法,返回该对象
    public static synchronized Singleton getSingleton() {
        // 对象为空,去实例化。不为空,直接返回
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

懒汉式(double check)

特点:懒加载(延迟加载)、线程安全、JDK1.5之后

缺点:实现较复杂

总结:采用双锁机制,线程安全且能在多线程情况下保持高性能。

只有在真正使用的时候,才开始初始化

public class Singleton {
    // 私有化构造函数
    private Singleton (){}
    // 声明该对象,但不会初始化,用到时候才会去实例化它
    // volatile 关键字,防止编译器对指令重排
    private volatile static Singleton singleton;
    // 调用此方法,返回该对象
    public static Singleton getInstance() {
        // 对象为空,去实例化。不为空,直接返回
        if (singleton == null) {
            // 加锁校验
            synchronized (Singleton.class) {
                // 双重验证
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

登记式/静态内部类

特点:懒加载,线程安全,实现难度一般

缺点:

总结:这种方式能达到双检锁方式一样的功能,实现更简单。充分利用了静态内部类的特点,对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

public class Singleton {
    private Singleton (){}

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

枚举式

特点:非懒加载、线程安全、实现简单、有自己的反序列化机制,不支持反射创建,JDK1.5之后

缺点:

总结:推荐写法,简单高效。充分利用枚举类的特性,只定义了一个实例,且枚举类是天然支持多线程的。只不过还没有被广泛应用

public enum Singletons {
    SINGLETON;
    public void getSingleton(){}
}

不建议使用懒汉式(第2,第3种方式),建议使用饿汉式(第1种方式)

只有在明确实现懒加载效果时,使用登记式(第5种方式)

如涉及到了反序列化创建对象时,强烈建议使用最后一种方式

如果有别的要求那就考虑使用双重验证锁方式(第4种方式)

源码中的单例

Spring & JDK
java.lang.Runtime
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry

Tomcat
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory 

反序列化指定数据源
java.util.Currency

通过反射攻击单例

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值