Java设计模式——单例模式

目录

引言

1. 饿汉式(静态常量)

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

3. 懒汉式(线程安全,同步方法)

4. 双重检查锁定

5. 静态内部类

6. 枚举

结论

引言

单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。本文将详细介绍六种不同的单例模式实现方法,并通过具体的Java代码示例来展示它们的原理及优缺点。

1. 饿汉式(静态常量)

这种方法在类加载时就完成了初始化,因此避免了线程同步问题,但可能会浪费内存空间。

代码示例

public class SingletonEager {
    // 在类加载时就创建实例
    private static final SingletonEager INSTANCE = new SingletonEager();

    private SingletonEager() {}

    /**
     * 提供获取实例的方法
     * @return 单例对象
     */
    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

优点

  • 实例化在类加载时完成,无需额外同步处理。
  • 实现简单。

缺点

  • 类加载时即创建实例,可能会造成资源浪费。

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

该方法在第一次调用 getInstance() 方法时创建实例。这种方式是最基本的懒汉式实现,但在多线程环境下可能会出现问题。

代码示例

public class SingletonLazy {
    private static SingletonLazy instance;

    private SingletonLazy() {}

    /**
     * 获取单例对象
     * @return 单例对象
     */
    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

优点

  • 实例化延迟到首次调用时,节省资源。

缺点

  • 非线程安全,在多线程环境中可能会导致多个实例被创建。

3. 懒汉式(线程安全,同步方法)

为了解决懒汉式的线程安全问题,可以在 getInstance() 方法上加锁,但这会导致性能下降。

代码示例

public class SingletonLazySync {
    private static SingletonLazySync instance;

    private SingletonLazySync() {}

    /**
     * 获取单例对象
     * @return 单例对象
     */
    public static synchronized SingletonLazySync getInstance() {
        if (instance == null) {
            instance = new SingletonLazySync();
        }
        return instance;
    }
}

优点

  • 线程安全。

缺点

  • 每次调用 getInstance() 都需要同步,影响性能。

4. 双重检查锁定

双重检查锁定是解决线程安全问题同时保持高效率的一种方式。

代码示例

public class SingletonDCL {
    private volatile static SingletonDCL instance;

    private SingletonDCL() {}

    /**
     * 获取单例对象
     * @return 单例对象
     */
    public static SingletonDCL getInstance() {
        if (instance == null) {
            synchronized (SingletonDCL.class) {
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

优点

  • 线程安全,且只在必要时同步。

  • 性能较高。

缺点

  • 实现较为复杂。

5. 静态内部类

利用Java的类加载机制保证初始化实例时只有一个线程,既解决了线程安全问题又保证了效率。

代码示例

public class SingletonInnerClass {
    private SingletonInnerClass() {}

    private static class SingletonHolder {
        private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
    }

    /**
     * 获取单例对象
     * @return 单例对象
     */
    public static SingletonInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点

  • 线程安全,无需同步。

  • 实现简单,延迟加载。

缺点

  • 相对于饿汉式来说,实现稍微复杂一些。

6. 枚举

枚举类型的实例天然就是单例的,并且可以防止反射和序列化带来的问题。

代码示例

public enum SingletonEnum {
    INSTANCE;

    public void someMethod() {
        // 实现方法
    }
}

优点

  • 自然地支持单例模式。

  • 保证线程安全。

  • 防止反射攻击。

缺点

  • 如果不需要其他特性,仅使用枚举作为单例可能显得有些过头。

结论

根据上述分析,每种单例模式的实现方式都有其特定的适用场景。如果要在这些实现方式中推荐一种最常用且适用性较广的方式,我会推荐 静态内部类 实现方式。以下是推荐的理由:

推荐理由 - 静态内部类

  1. 线程安全性:静态内部类利用了Java类加载机制的特性,保证了实例化的线程安全性。由于静态内部类只会被加载一次,因此可以确保实例化过程只发生一次,不会出现多线程环境下的并发问题。

  2. 延迟加载:静态内部类中的实例是在首次调用 getInstance() 方法时才被创建的,实现了延迟加载,这样可以在需要的时候才创建实例,节省了不必要的内存占用。

  3. 简洁性:相比于双重检查锁定等实现方式,静态内部类的实现更为简洁明了,易于理解。

  4. 效率:静态内部类在多线程环境中表现良好,因为它不需要在每次调用 getInstance() 时都进行同步操作。

  5. 可扩展性:如果将来需要扩展单例类的功能,静态内部类的实现方式也相对容易扩展,例如添加更多的静态成员变量或方法。

  • 21
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值