设计模式专题

设计模式-单例模式

2022-08-30  本文已影响0人  RUMyCola

设计模式-单例模式

定义

单例模式(singleton pattern)是确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点

单例模式是创建型模式。

分类

1、饿汉式(饿汉和静态饿汉)

2、懒汉式(线程安全要加锁)

3、枚举式(注册)初始化即生成,饿汉式,可以保证不被反射机制和反序列化破坏,是借助JDK的特性,所以最官方、最权威、最稳定

4、序列化单例模式(可以解决反序列化问题,但是内存开销依然很大)

5、容器式单例模式(容器式单例模式适用于需要大量创建单例对象的场景,便于管理。但它是非线程安全的)

6、threadLocal单例模式(不能保证其创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,天生是线程安全的)

总结

    单例模式可以保证内存中只有一个实例,减少了内存开销,还可以避免对资源的多重占用。

单例模式的常见应⽤场景

单例模式(Singleton)也叫单态模式,是设计模式中最为简单的⼀种模式,甚⾄有些模式⼤师都不称其为模式,称其为⼀种实现技巧,因为设计模式讲究对象之间的关系的抽象,⽽单例模式只有⾃⼰⼀个对象,也因此有些设计⼤师并把把其称为设计模式之⼀。

好多没怎么使⽤过的⼈可能会想,单例模式感觉不怎么⽤到,实际的应⽤场景有哪些呢?以下,我将列出⼀些就在咱们周边和很有意义的单例应⽤场景。

1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你⾃⼰试试看哦~

2. windows的Recycle Bin(回收站)也是典型的单例应⽤。在整个系统运⾏过程中,回收站⼀直维护着仅有的⼀个实例。

3. ⽹站的计数器,⼀般也是采⽤单例模式实现,否则难以同步。

4. 应⽤程序的⽇志应⽤,⼀般都何⽤单例模式实现,这⼀般是由于共享的⽇志⽂件⼀直处于打开状态,因为只能有⼀个实例去操作,否则内容不好追加。

5. Web应⽤的配置对象的读取,⼀般也应⽤单例模式,这个是由于配置⽂件是共享的资源。

6. 数据库连接池的设计⼀般也是采⽤单例模式,因为数据库连接是⼀种数据库资源。数据库软件系统中使⽤数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是⾮常昂贵的,因为何⽤单例模式来维护,就可以⼤⼤降低这种损耗。

7. 多线程的线程池的设计⼀般也是采⽤单例模式,这是由于线程池要⽅便对池中的线程进⾏控制。

8. 操作系统的⽂件系统,也是⼤的单例模式实现的具体例⼦,⼀个操作系统只能有⼀个⽂件系统。

9. HttpApplication 也是单位例的典型应⽤。熟悉ASP.Net(IIS)的整个请求⽣命周期的⼈应该知道HttpApplication也是单例模式,所有的HttpModule都共享⼀个HttpApplication实例.

  总结以上,不难看出:

单例模式应⽤的场景⼀般发现在以下条件下:

(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的⽇志⽂件,应⽤配置。

(2)控制资源的情况下,⽅便资源之间的互相通信。如线程池等。

单例模式优点:

1、在内存中只有一个实例,减少了内存开销。

2、可以避免资源的多重占用。

3、设置全局访问点,严格控制访问。

单例模式的缺点:

1、没有接口,扩展困难

2、如果要扩展单例对象,只有修改代码,没有其他途径。

代码实例

1.1、饿汉式单例模式:类加载时立即初始化,并且创建单例对象。它绝对线程安全,在线程还没出现就实例化了,不存在访问安全问题。但是会导致内存浪费. 1.2、饿汉式单例模式(静态代码块):同第一种,唯一不同就是多点逼格O .O 2.1懒汉式单例模式,添加synchronized关键字后,解决了传统懒汉模式的线程安全问题,但是每次都要获取锁,性能较低. 2.2懒汉式单例模式改进(双重检查锁):可以有效的提高运行效率,那么我们可不可以不使用锁? 2.3懒汉式单例模式改进(静态内部类):这种形式来兼顾饿汉式单例模式的内存浪费问题和 synchronized 的性能问题(完美的屏蔽了这两个缺点) ,它还可以防止被反射破坏单例机制.到目前为止,这种写法基本上已经是最牛的啦,,但是依然可以被反序列化破坏(逐渐开始杠精起来...) 3.1、枚举类单例模式(属于注册式单例模式的一种):初始化即生成,饿汉式,可以保证不被反射机制和反序列化破坏,是借助JDK的特性,所以最官方、最权威、最稳定,因此枚举式单例模式是《Effective java》书中推荐的一种单例模式实现方法。 3.2、容器式单例模式(属于注册式单例模式的第2种):适用于需要大量创建单例对象的场景,便于管理。但它是非线程安全的。 4、序列化单例模式:反序列化导致单例模式被破坏,加上readResolve方法,可以解决反序列化问题,但是内存开销依然很大. 5、线程单例实现ThreadLocal:不能保证其创建的对象是全局唯一的,但是能保证在单个线程中是唯一的,天生是线程安全的。这个方式适合一些特定场景.

学习单例模式的知识重点总结

1、私有化构造器

2、保证线程安全

单例模式可以保证内存里只有一个实例,减少了内存的开销,还可以避免对资源的多重占用。单例模式看起来非常简单,实现起来其实也非常简单,但是在面试中却是一个高频面试点。希望“小伙伴们”通过本章的学习,对单例模式有了非常深刻的认识,在面试中彰显技术深度,提升核心竞争力,给面试加分,顺利拿到录取通知(Offer)。

扩展

1、解决容器式单例的线程安全问题。

两种方法:双重检查锁,利用ConcurrentHashMap#putIfAbsent()方法的原子性。

public class ContainerSingleton {

    private static Map ioc = new ConcurrentHashMap();

    private ContainerSingleton() {

            throw new RuntimeException("不可被实例化!");

    }

    // 方法一:双重检查锁

    public static Object getInstance(String className) {

        Object instance = null;

        if (!ioc.containsKey(className)) {

            synchronized (ContainerSingleton.class) {

                if (!ioc.containsKey(className)) {

                    try {

                        instance = Class.forName(className).newInstance();

                        ioc.put(className, instance);

                    } catch (Exception e) {

                        e.printStackTrace();

                    }

                return instance;

                } else {

                    return ioc.get(className);

                }

            }

        }

        return ioc.get(className);

    }

    // 方法二:利用ConcurrentHashMap#putIfAbsent()方法的原子性

public static Object getInstance1(String className){

    Object instance = null;

    try {

        ioc.putIfAbsent(className, Class.forName(className).newInstance());

    }catch (Exception e){

        e.printStackTrace();

    }

    return ioc.get(className);

    }

}

上一篇下一篇

猜你喜欢

热点阅读