一文彻底学会单例模式

一、为什么使用单例模式?

单例模式是一种创建型设计模式,目的是确保一个类在应用程序中只有一个实例,并提供全局访问点。以下是使用单例模式的几个好处

  • 资源节省:对于需要频繁创建和销毁的对象,比如数据库连接或日志记录器,使用单例模式可以节约系统资源。
  • 全局访问:提供一个全局的访问点,便于访问共享资源。
  • 控制实例数量:确保一个类只有一个实例,避免对资源的多重占用。

二、单例模式是什么?

单例模式限制类的实例化次数为一次,并通过提供一个静态方法来获取该实例。下面是几种常见的实现方式:

  • 懒汉式:实例在第一次使用时创建,不适用于多线程环境。
  • 饿汉式:类加载时即创建实例,线程安全。
  • 双重检查锁定:结合懒汉和饿汉的优点,适用于多线程。
  • 静态内部类:利用JVM的类加载机制实现线程安全和延迟加载。

三、怎么用单例模式?

一、懒汉式(线程不安全)

这种方式只适合单线程使用,多线程下有会实例化多个对象

public class Single {
    private static Single instance;
 
    private Single(){
        System.out.println("实例化Single对象");
    }
    public static Single getInstance(){
        if (instance == null) instance = new Single();
        return instance;
    }
}
public class test {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            Single.getInstance();
        }
    }
}
测试结果:
    /*
    实例化Single对象
    Process finished with exit code 0
    */
二、懒汉式(线程安全)

只需添加一个synchronized 关键字即可,也就是常见的加锁操作。

public class Single {
    private static Single instance;
 
    private Single(){
        System.out.println("实例化Single对象");
    }
    public synchronized static Single getInstance(){
        if (instance == null) instance = new Single();
        return instance;
    }
}
public class test {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                Single.getInstance();
            }).start();
        }
    }
}
测试结果:
    /*
    实例化Single对象
    Process finished with exit code 0
    */

在多线程环境中使用 synchronized 可以确保实例只被创建一次,但它会导致系统资源的浪费。假设我们进行10次调用,每次都会经过加锁过程,实际上我们只需在第一次实例化时加锁。后续的调用仅需要进行逻辑判断,而不必再实例化对象。因此,我们可以引入一种优化方法,减少锁的使用频率,也就是在类加载时直接实例化对象。

三、饿汉式

在饿汉式单例模式中,类的实例是在类加载时就被创建的,这样确保了线程安全和唯一性,但也可能导致资源浪费,因为无论是否需要这个实例,都会在启动时被创建。

public class Single {
   // 在类加载时就创建实例 无论使用不使用 都会创建  会造成资源浪费
   private static final Single instance =new Single();
 
    private Single(){
        System.out.println("实例化Single对象");
    }
    public  static Single getInstance(){
        return instance;
    }
}
四、双重检查锁定:结合懒汉和饿汉的优点,适用于多线程。

双重检查锁定旨在减少不必要的同步,提高性能。它结合了懒汉式和饿汉式的优点:

  • 懒汉式加载:只有在需要时才创建实例。
  • 线程安全:通过同步块和双重检查,确保多线程环境下实例的唯一性。
public class Singleton {
    private volatile static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
    //第一次检查:避免不必要的同步,提高性能。如果实例已存在,直接返回。
        if (instance == null) {
            synchronized (Singleton.class) {
            //第二次检查:在同步块内再次检查,确保实例未被其他线程创建。只在第一次时创建实例。
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
五、静态内部类。

静态内部类利用JVM类加载机制,在类加载时并不会立即创建实例,而是在调用 getInstance() 方法时才创建,实现了延迟加载和线程安全。
静态内部类的特性,JVM在加载外部类时不会加载内部类,这样可以实现懒加载并且是线程安全的:

public class Singleton {
    private Singleton() {}

    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

适用场景:
单例模式适用于需要频繁创建和销毁的对象、创建对象耗时较多但又经常用到的对象、工具类对象等。
优缺点:

  • 优点:内存中只有一个实例,减少内存开销,避免多重占用。
  • 缺点:不利于扩展,单例类职责过重,在一定程度上违反了单一职责原则。
    注意事项:
  • 在多线程环境下,要确保线程安全。
  • 如果使用反射机制或者序列化机制,需要额外处理以确保单例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kimloner

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值