设计模式四:单例模式(Singleton)

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。
通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。
要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。

单例模式的分类

单例模式可以根据实现方式的不同,分为以下几种分类:

  1. 饿汉式(Eager Initialization):在类加载的时候就创建并初始化单例对象。这种方式简单直接,线程安全,但可能会造成资源浪费,因为即使没有使用单例对象,它也会被提前创建。
  2. 懒汉式(Lazy Initialization):在第一次使用时创建单例对象。这种方式避免了不必要的资源浪费,但需要考虑线程安全性,确保在多线程环境下仍然能够正确地创建单例对象。
  3. 双重检验锁(Double-Checked Locking):结合了懒汉式和饿汉式的优点,在多线程环境下既能保证线程安全,又能延迟单例对象的创建。这种方式通过加锁来控制并发访问,并使用 volatile 关键字来保证可见性。
  4. 静态内部类(Static Inner Class):利用类加载机制来实现延迟加载和线程安全的单例对象。通过定义一个私有的静态内部类,在这个静态内部类中创建单例对象,从而保证只有在第一次使用时才会触发单例对象的初始化。
  5. 枚举(Enum):枚举类型本身就是单例的。在Java中,枚举类型能够保证在任何情况下都只有一个实例。因此,可以直接使用枚举来实现单例模式。
    这些分类方式基本上涵盖了常见的单例模式实现方式。根据具体的需求和场景,选择适合的单例模式实现方式可以提高代码的可靠性和性能效率。

Singleton模式的使用场景

单例模式是一种常见的设计模式,在以下情况下可以考虑使用单例模式:
1.全局资源共享:当应用程序需要在多个部分共享同一个资源时,可以使用单例模式确保只有一个实例存在。例如,数据库连接池、日志记录器等全局资源可以使用单例模式来管理和访问。
2.对象缓存:当需要缓存对象以提高性能时,可以使用单例模式来管理缓存。通过保持单例实例,可以避免重复创建对象,并且在需要时可以快速获取缓存对象。
3.配置信息管理:当应用程序需要维护一些全局配置信息时,可以使用单例模式来管理这些配置。这样可以确保只有一个实例保存和管理配置信息,并且可以在程序的各个地方使用。
4.日志记录器:在应用程序中,通常需要一个日志记录器来记录系统的操作和异常信息。通过使用单例模式,可以方便地在代码的任何地方访问和使用统一的日志记录器。
5.系统计数器:某些场景需要记录系统某些操作的次数,如请求处理次数、任务处理次数等。使用单例模式可以方便地实现对统计数据的更新和访问。
需要注意的是,单例模式并不适用于所有的场景。在一些情况下,它可能会导致代码的复杂性增加,或者造成不必要的性能开销。

饿汉式

指全局的单例实例在类装载时构建。它是线程安全的,但是如果这个类我一直不使用,由于类初始化时,就已经实例它了,所以它会一直占着资源不释放。

/**
 * 单例模式
 */
public class Singleton {
    // 饿汉式
    private static Singleton instance2 = new Singleton();
    public static Singleton getInstance3(){
        return instance2;
    }
}

懒汉式–线程不安全

最基础的实现方式,线程上下文单例,不需要共享给所有线程,也不需要加synchronize之类的锁,以提高性能,有个致命缺点,就是在两个相同的线程中同时调用了getInstance1() 时,就会在这两个线程中产生不同的Singleton 对象。单例的作用就相当没有了。由于它的线程不安全,所以有了下面的方式。

/**
 * 单例模式
 */
public class Singleton {
    private static Singleton instance;
    // 懒汉式—线程不安全
    public static Singleton getInstance1(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式–线程安全

加上synchronize之类保证线程安全的基础上的懒汉模式,相对性能很低,大部分时间并不需要同步。

/**
 * 单例模式
 */
public class Singleton {
    private static Singleton instance;
    // 懒汉式—线程安全
    public static synchronized Singleton getInstance2(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

它是线程安全了;但由于它是同步方法,在多线程调用它时,都会synchronized下,从而效率低下。在使用的过程中为了提高效率,所以我们有了如下方式

双重检验锁

在懒汉式基础上利用synchronize关键字和volatile关键字确保第一次创建时没有线程间竞争而产生多个实例,仅第一次创建时同步,性能相对较高

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
        // 私有构造函数,防止外部创建实例
    }

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

在上述代码中,getInstance() 方法使用了双重检验锁,首先检查 instance 是否为 null,如果是,则再进入同步块进行二次检查,确保只有一个线程能够创建实例。同时,为了避免由于指令重排序而导致的问题,需要给 instance 声明为 volatile,保证可见性。

静态内部类(登记式)

创建类的全局属性存在,创建类被装载时创建。

public class Singleton {
    private static Map<String, Singleton> registry = new HashMap<>();
    
    private Singleton() {
        // 私有构造函数,防止外部实例化
    }
    
    public static synchronized Singleton getInstance(String key) {
        if (!registry.containsKey(key)) {
            registry.put(key, new Singleton());
        }
        return registry.get(key);
    }
}

第一次记载Singleton 时并不会初始化instance,只有第一次调用getInstance4()时才会实例化。它不仅保证线程安全、也能保证对象的唯一性,同时也延迟了单例的实例化。它也是最为推荐的一种单例模式

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值