Java设计模式——单例模式

目录

引言

[1. 饿汉式(静态常量)](#1. 饿汉式(静态常量))

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

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

[4. 双重检查锁定](#4. 双重检查锁定)

[5. 静态内部类](#5. 静态内部类)

[6. 枚举](#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. 可扩展性:如果将来需要扩展单例类的功能,静态内部类的实现方式也相对容易扩展,例如添加更多的静态成员变量或方法。

相关推荐
需要82626 分钟前
Arthas 一个命令排查线上问题
java·jvm·spring·spring cloud
Kyrie_kk38 分钟前
Java--ProcessBuilder操作系统进程
java·后端
SamDeepThinking43 分钟前
关于java final关键字的可见性
java·后端·面试
怕浪猫1 小时前
一行命令复刻爆款视频,我把 Hypit 从安装跑到了出片
人工智能·设计模式·程序员
Wang's Blog1 小时前
Java 项目实战: 外卖平台-MyBatis-Plus分页插件与员工分页查询
java·项目开发
MetaLite1 小时前
全网最好的SpringBoot接口请求对象设计
java·spring boot·后端
蜗牛互联网1 小时前
AI给面试打分不够,求职者更需要可核对的证据
java·人工智能·后端
此时不提桶,更待何时2 小时前
02-05-B-虚拟线程面试与生产事故实战
java·面试
名字还没想好☜2 小时前
Spring Boot 3.2 RestClient 实战:替代 RestTemplate 调外部接口,超时、连接池与错误处理
java·后端·spring
摇滚侠2 小时前
《Spring Boot 3:高级与架构设计》第 2 章 IOC容器的高级机制 BeanFactoryPostProcessor 个人理解 6
java·spring boot·笔记·后端