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

相关推荐
测开小菜鸟5 分钟前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity1 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天1 小时前
java的threadlocal为何内存泄漏
java
caridle1 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
^velpro^1 小时前
数据库连接池的创建
java·开发语言·数据库
苹果醋31 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
秋の花2 小时前
【JAVA基础】Java集合基础
java·开发语言·windows
小松学前端2 小时前
第六章 7.0 LinkList
java·开发语言·网络
Wx-bishekaifayuan2 小时前
django电商易购系统-计算机设计毕业源码61059
java·spring boot·spring·spring cloud·django·sqlite·guava
customer082 小时前
【开源免费】基于SpringBoot+Vue.JS周边产品销售网站(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·java-ee·开源