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

目录

什么是单例模式

单例模式的类型

懒汉式:

饿汉式:

双重检查锁式:

静态内部类式:

枚举式:

登记式:

单例模式的特点

特点

优点

缺点

适用场景

使用场景


        在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例,这些应用都或多或少具有资源管理器的功能。选择单例模式就是为了避免不一致状态,同时也可以避免内存浪费。        

        单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、双重检查锁单例、静态内部类单例、枚举单例、登记式单例。

什么是单例模式

单例模式:属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例,该类自行实例化并向整个系统提供这个实例的公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

单例模式的类型

懒汉式:

1.懒汉模式(线程不安全)

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 懒汉模式(线程不安全)
 * @date 2023/04/02 09:55:33
 */
public class Singleton_Lazy_1 {
    
    private static Singleton_Lazy_1 instance;

    private Singleton_Lazy_1() {
    }

    public static synchronized Singleton_Lazy_1 getInstance() {
        if (null != instance) {
            return instance;
        }
        instance = new Singleton_Lazy_1();
        return instance;
    }
}

此种模式是线程不安全,但把锁加到方法上,每次访问都因需要锁占用导致资源的浪费。除非特殊情况下,不建议此种方式实现单例模式。

2.懒汉模式(线程安全)

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 懒汉模式(线程安全)
 * @date 2023/04/02 09:55:33
 */
public class Singleton_Lazy_2 {
    
    private static Singleton_Lazy_2 instance;

    private Singleton_Lazy_2() {
    }

    public static synchronized Singleton_Lazy_2 getInstance() {
        if (null != instance) {
            return instance;
        }
        instance = new Singleton_Lazy_2();
        return instance;
    }
}

        此种模式是线程安全,当方法getInstance()出现并发访问时,会出现多个实例的情况,不建议此种方式实现单例模式。

饿汉式:

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 饿汉模式(线程安全)
 * @date 2023/04/02 09:30:33
 */
public class Singleton_Hungry_1 {

    private static Singleton_Hungry_1 instance = new Singleton_Hungry_1();

    private Singleton_Hungry_1() {
    }

    public static Singleton_Hungry_1 getInstance() {
        return instance;
    }
}

双重检查锁式:

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 双重检查锁模式(线程安全)
 * @date 2023/04/02 09:55:33
 */
public class Singleton_Lock_1 {

    private static volatile Singleton_Lock_1 instance;

    private Singleton_Lock_1() {
    }

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

        执行双重检测原因:如果多个线程通过了第一次检测,此时因为synchronized,其中一个线程会首先通过了第二次检测并实例化了对象,剩余的线程不会再重复实例化对象。这样,除了初始化的时候会加锁,后续的调用都是直接返回,解决了多余的性能消耗。

外层判断:完成实例化后,之后的线程就不需要再执行synchronized等待,提高效率。
内层判断:防止多次实例化。

静态内部类式:

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 静态内部类模式(线程安全)
 * @date 2023/04/02 09:55:33
 */
public class Singleton_Inner_1 {
    
    private Singleton_Inner_1() {
    }
    private static class InnerSingleton {
        static Singleton_Inner_1 instance = new Singleton_Inner_1();
    }

    public static Singleton_Inner_1 getInstance() {
        return InnerSingleton.instance;
    }
}

        资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法,但是第一次加载时反应不够快。

枚举式:

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 枚举模式(线程安全)
 * @date 2023/04/02 10:34:39
 */
public enum Singleton_Enum_1 {
    INSTANCE;
    public void businessMethod() {
        System.out.println("昂焱数据 我是一个单例!");
    }
}

        Effective Java 作者推荐使用枚举的方式解决单例模式,此种方式可能是平时最少用到的。
这种方式解决了最主要的;线程安全、自由串行化、单一实例。
        这种写法在功能上与共有域方法相近,但是它更简洁,无偿地提供了串行化机制,绝对防止对此实例化,即使是在面对复杂的串行化或者反射攻击的时候。虽然该方法还没有广泛采用,但是单元素的枚举类型已经成为实现Singleton的最佳方法。

登记式:

import java.util.HashMap;

/**
 * @author Evan Walker
 * @version 1.0
 * @web 昂焱数据 https://www.ayshuju.com
 * @desc 单例的 登记模式
 * @date 2023/04/02 09:55:33
 */
public class Singleton_Registry_1 {

    /**
     * 使用一个map来当注册表
     */
    private static HashMap registry = new HashMap();

    //静态块,在类被加载时自动执行,把Registry Singleton自己也纳入容器管理
    static {
        Singleton_Registry_1 rs = new Singleton_Registry_1();
        registry.put(rs.getClass().getName(), rs);
    }

    /**
     * 受保护的默认构造函数,如果为继承关系,则可以调用,克服了单例类不能为继承的缺点
     */
    protected Singleton_Registry_1() {
    }

    /**
     * 静态工厂方法,返回此类的唯一实例
     */
    public static Singleton_Registry_1 getInstance(String name) {
        if (name == null) {
            name = Singleton_Registry_1.class.getName();
        }
        if (registry.get(name) == null) {
            try {
                registry.put(name, Class.forName(name).newInstance());
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return (Singleton_Registry_1) registry.get(name);
    }

}

        登记式单例将多种的单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

单例模式的特点

特点

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

优点

  1. 系统中只存在一个共用的实例对象,无需频繁创建和销毁对象,节约了系统资源,提高系统的性能
  2. 可以严格控制客户怎么样以及何时访问单例对象。

缺点

  1. 可能的开发混淆,使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
  2. 不适用于变化的对象,单例对象的拓展有很大的困难。

适用场景

  1. 需要频繁实例化然后销毁的对象。 
  2. 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
  3. 有状态的工具类对象。 
  4. 频繁访问数据库或文件的对象。 

使用场景

  1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
  2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。 

更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值