几种单例模式

单例模式

单例模式(Singleton Pattern)是一种设计模式,目的是确保某个类只有一个实例,并且提供一个全局的访问点。通常在需要控制资源共享、节省内存等场景下使用单例模式。

1. 饿汉式(Eager Initialization)

饿汉式的单例模式在类加载时就创建实例,这样的好处是线程安全的,因为在类加载时实例已经创建好。但是它的缺点是,如果这个单例类没有被使用,还是会创建这个实例,浪费了内存。

代码实现:

public class Singleton {
    // 在类加载时就初始化
    private static Singleton singleton = new Singleton();

    // 私有构造函数
    private Singleton() {
        System.out.println("生成了一个实例。");
    }

    // 获取唯一实例
    public static Singleton getInstance() {
        return singleton;
    }
}

优点:

  • 线程安全,因为实例是在类加载时就创建的,保证了实例的唯一性。
  • 没有加锁,性能较好。

缺点:

  • 即使没有使用该实例,类加载时就已经创建了实例,浪费了资源。
2. 懒汉式(Lazy Initialization)

懒汉式单例模式的特点是延迟实例化,只有在第一次调用 getInstance() 时才创建实例。它的缺点是线程不安全,因为多个线程可能会在同一时刻同时通过 if(singleton==null) 判断,从而导致创建多个实例。

代码实现(线程不安全):

public class Singleton {
    private static Singleton singleton = null;

    private Singleton() {
        System.out.println("生成了一个实例。");
    }

    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

优点:

  • 在类加载时不会创建实例,节省资源。
  • 只有在需要时才创建实例,延迟加载。

缺点:

  • 线程不安全,在并发情况下可能会创建多个实例。
3. 线程安全的懒汉式(Synchronized)

为了确保多线程环境下的安全性,可以在 getInstance() 方法上加上 synchronized 关键字,避免多个线程同时进入该方法创建多个实例。这样做可以保证线程安全,但加锁会导致性能下降,尤其在并发量大的情况下。

代码实现:

public class Singleton {
    private static Singleton singleton = null;

    private Singleton() {
        System.out.println("生成了一个实例。");
    }

    // 使用 synchronized 保证线程安全
    public synchronized static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}

优点:

  • 保证了线程安全,适合单线程环境。

缺点:

  • 性能开销较大,因为每次调用 getInstance() 方法时都需要获取锁,锁的操作在并发环境中会降低效率。
4. 双重检查锁(Double-Checked Locking)

双重检查锁定通过在 synchronized 块内部再次判断 singleton == null 来避免每次都加锁,提高了性能。第一次判断不加锁,第二次判断时才加锁。

代码实现:

public class Singleton {
    private static volatile Singleton singleton = null;

    private Singleton() {
        System.out.println("生成了一个实例。");
    }

    public static Singleton getInstance() {
        if (singleton == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (singleton == null) { // 第二次检查
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

优点:

  • 性能更好,只有第一次创建实例时需要加锁,后续调用不需要锁定。

缺点:

  • 需要使用 volatile 关键字确保内存可见性,避免多线程环境下的 singleton 变量的缓存问题。
5. 静态内部类实现单例(推荐方式)

这种方式利用了 Java 类加载的机制来保证线程安全,同时也能避免不必要的同步开销。它利用了 Java 的类加载机制,只有在 getInstance() 被调用时,SingletonHolder 才会被加载,并且 Singleton 类的实例才会被创建。

代码实现:

public class Singleton {
    // 静态内部类,只有在第一次使用时才会被加载
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {
        System.out.println("生成了一个实例。");
    }

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

优点:

  • 延迟加载,只有在调用 getInstance() 时才会创建实例。
  • 线程安全,利用了类加载机制保证了线程安全。
  • 不需要加锁,提高了性能。

缺点:

  • 这种方式依赖于 JVM 的类加载机制,较为复杂,但通常推荐使用这种方式,因为它是线程安全且高效的。

总结:

  • 饿汉式:线程安全,但会在类加载时就创建实例,浪费资源。
  • 懒汉式:延迟加载,避免资源浪费,但线程不安全。
  • 线程安全的懒汉式:使用 synchronized 保证线程安全,但性能较差。
  • 双重检查锁:优化了线程安全,避免了每次加锁,但代码较复杂。
  • 静态内部类:推荐的单例实现方式,线程安全且性能高。

根据实际需求选择合适的单例实现方式。通常推荐使用 静态内部类实现单例,因为它既线程安全又高效。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值