问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

深度学习设计模式之单例模式

创作时间:
作者:
@小白创作中心

深度学习设计模式之单例模式

引用
CSDN
1.
https://m.blog.csdn.net/G_whang/article/details/138678916

单例模式是软件设计模式中最基础且应用最广泛的一种,特别是在深度学习等需要高效资源管理的场景中。本文将深入探讨单例模式的实现方式、优缺点以及在实际开发中的应用,帮助读者全面理解这一重要设计模式。

一、单例模式简介

单例模式确保一个类只有一个实例,并提供一个全局访问点。这种模式在需要控制资源访问和管理的场景中特别有用,例如数据库连接池、配置文件管理器和缓存系统等。

二、单例模式实现步骤

单例模式的实现通常包括以下几个关键步骤:

  1. 使用一个私有构造函数,防止外部直接创建实例
  2. 定义一个私有静态变量来保存唯一的实例
  3. 提供一个公有静态方法来获取该实例

通过这些步骤,可以确保类的实例在程序运行期间只被创建一次。

三、单例模式的两种方式

1.懒汉模式

懒汉模式的核心思想是“按需创建”,即只有在真正需要对象时才进行实例化。这种方式可以节省资源,但如果处理不当,在多线程环境下可能会导致多个实例的创建。

1.1 简易版懒汉模式

public class LazySingleton {
    /**
     * 成员变量
     */
    private static LazySingleton instance;
    /**
     * 构造方法私有化
     */
    private LazySingleton(){
    }
    /**
     * 获取单例对象
     * @return
     */
    public static LazySingleton getInstance(){
        if(instance == null){
            System.out.println("创建实例");
            instance = new LazySingleton();
            return instance;
        }
        System.out.println("实例对象已存在,无需再创建");
        return instance;
    }
}

测试类

public static void main(String[] args) {
    // 先创建一个对象,看是否有输出
    LazySingleton instance = LazySingleton.getInstance();
    LazySingleton instance1 = LazySingleton.getInstance();
}

结果:简易版的单例模式在多线程环境下存在线程安全问题,可能会创建多个实例。

1.2 线程安全的单例模式

为了解决多线程问题,可以在getInstance()方法上添加synchronized关键字。

public class LazySingleton {
    /**
     * 成员变量
     */
    private static LazySingleton instance;
    /**
     * 构造方法私有化
     */
    private LazySingleton(){
    }
    /**
     * 获取单例对象
     * @return
     */
    public static synchronized LazySingleton getInstance(){
        if(instance == null){
            System.out.println("创建实例");
            instance = new LazySingleton();
            return instance;
        }
        System.out.println("实例对象已存在,无需再创建");
        return instance;
    }
}

这种方法虽然解决了多线程问题,但在性能上有所损失,因为每次调用getInstance()都会进行同步。

1.3 线程安全的单例模式V2.0版

为了优化性能,可以将synchronized关键字应用到代码块中,仅在创建实例时进行同步。

public class LazySingleton {
    /**
     * 成员变量
     */
    private static LazySingleton instance;
    /**
     * 构造方法私有化
     */
    private LazySingleton(){
    }
    /**
     * 获取单例对象
     * @return
     */
    public static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class){
                System.out.println("创建实例");
                instance = new LazySingleton();
            }
            return instance;
        }
        System.out.println("实例对象已存在,无需再创建");
        return instance;
    }
}

这种方式虽然减少了锁的粒度,但在某些情况下仍可能创建多个实例。

1.3 线程安全的单例模式V2.1版-双重校验锁

通过增加双重检查机制,可以进一步优化线程安全问题。

public class LazySingleton {
    /**
     * 成员变量
     */
    private static LazySingleton instance;
    /**
     * 构造方法私有化
     */
    private LazySingleton(){
    }
    /**
     * 获取单例对象
     * @return
     */
    public static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class){
                if(instance == null){
                    System.out.println("创建实例");
                    instance = new LazySingleton();
                }
            }
            return instance;
        }
        System.out.println("实例对象已存在,无需再创建");
        return instance;
    }
}

1.3 线程安全的单例模式V3.0版-双重校验锁终极版本

为了解决指令重排序问题,可以使用volatile关键字修饰实例变量。

public class LazySingleton {
    /**
     * 成员变量
     */
    private static volatile LazySingleton instance;
    /**
     * 构造方法私有化
     */
    private LazySingleton(){
    }
    /**
     * 获取单例对象
     * @return
     */
    public static LazySingleton getInstance(){
        if(instance == null){
            synchronized (LazySingleton.class){
                if(instance == null){
                    System.out.println("创建实例");
                    instance = new LazySingleton();
                }
            }
            return instance;
        }
        System.out.println("实例对象已存在,无需再创建");
        return instance;
    }
}

2.饿汉模式

饿汉模式在类加载时就创建实例,简单且线程安全,但可能会造成资源浪费。

public class HungrySingleton {
    // 一开始就初始化对象
    private static HungrySingleton instance = new HungrySingleton();
    // 私有化构造方法
    private HungrySingleton(){}
    public static HungrySingleton getInstance(){
        return instance;
    }
}

四、扩展实现单例

1.使用枚举的方式实现单例模式

使用枚举可以避免反射等特殊情况下创建多个实例的问题。

public enum SingletonEnum {
    INSTANCE;
    public void test(){
        System.out.println("1111");
    }
}

2.使用内部类的方式实现单例模式

内部类方式结合了懒加载和线程安全的优点。

/**
 * 静态内部类方式实现单例
 */
public class Singleton {
    // 私有化构造方法
    private Singleton(){}
    public void test(){
        System.out.println("2222");
    }
    /**
     * 静态内部类
     */
    private static class SingletonHolder{
        // 初始化对象
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

FAQ

1.为什么要私有化构造方法?

单例模式的核心是保证对象只被实例化一次,因此需要将构造方法私有化,防止外部通过new关键字创建实例。

2.为什么成员变量要用 static 修饰?

由于构造方法被私有化,无法通过实例化对象来调用方法,因此需要使用静态方法来访问成员变量。为了使静态方法能够访问成员变量,需要将成员变量声明为静态。

3.单例模式的应用场景?

  • 数据库连接池:确保应用程序中只有一个数据库连接池实例,避免资源浪费。
  • 配置文件管理器:确保在整个应用程序中只有一个配置文件管理器实例。
  • 缓存系统:确保只有一个缓存实例,提高性能。

4.单例模式使用的注意情况

单例模式主要分为懒汉模式和饿汉模式。在选择使用哪种模式时,需要综合考虑资源占用和使用频率等因素。对于占用内存小的类,可以使用饿汉模式;对于占用内存较大的类,则更适合使用懒汉模式。

5.JDK中的单例

JDK中的一些类也使用了单例模式。例如,java.lang.Runtime类使用的是饿汉模式,而java.awt.Desktop类则使用了懒汉模式。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号