单例模式 (Singleton Pattern)

概述

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。

意图

  • 确保一个类只有一个实例
  • 提供一个全局访问点来访问这个实例

适用场景

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
  • 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时

结构

复制代码
┌─────────────┐
│  Singleton  │
├─────────────┤
│ - instance  │
├─────────────┤
│ + getInstance() │
└─────────────┘

参与者

  • Singleton:定义一个Instance操作,允许客户访问它的唯一实例。Instance是一个类操作。可能负责创建它自己的唯一实例。

实现方式

1. 饿汉式(线程安全)

java 复制代码
public class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {}
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

优点 :实现简单,类加载时就完成实例化,避免了线程同步问题
缺点:无法实现懒加载,可能造成内存浪费

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

java 复制代码
public class LazySingleton {
    private static LazySingleton instance;
    
    private LazySingleton() {}
    
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

优点 :实现了懒加载
缺点:多线程环境下不安全

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

java 复制代码
public class ThreadSafeLazySingleton {
    private static ThreadSafeLazySingleton instance;
    
    private ThreadSafeLazySingleton() {}
    
    public static synchronized ThreadSafeLazySingleton getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazySingleton();
        }
        return instance;
    }
}

优点 :解决了线程安全问题
缺点:效率低,每个线程获取实例时都需要同步

4. 双重检查锁定(DCL,推荐)

java 复制代码
public class DoubleCheckedSingleton {
    private static volatile DoubleCheckedSingleton instance;
    
    private DoubleCheckedSingleton() {}
    
    public static DoubleCheckedSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckedSingleton();
                }
            }
        }
        return instance;
    }
}

优点 :既实现了懒加载,又保证了线程安全,且效率较高
缺点:实现稍复杂

5. 静态内部类(推荐)

java 复制代码
public class StaticInnerClassSingleton {
    private StaticInnerClassSingleton() {}
    
    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }
    
    public static StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点 :实现了懒加载,线程安全,且实现简单
缺点:无

6. 枚举(最推荐)

java 复制代码
public enum EnumSingleton {
    INSTANCE;
    
    public void doSomething() {
        // 业务方法
    }
}

优点 :实现简单,线程安全,防止反序列化创建新对象
缺点:无法实现懒加载

示例代码

下面是一个完整的单例模式示例,使用双重检查锁定实现:

java 复制代码
/**
 * 数据库连接池单例类
 */
public class DatabaseConnectionPool {
    private static volatile DatabaseConnectionPool instance;
    private Connection connection;
    
    private DatabaseConnectionPool() {
        // 初始化数据库连接
        try {
            // 这里只是示例,实际应用中需要真实的数据库连接代码
            System.out.println("创建数据库连接池...");
            connection = createConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static DatabaseConnectionPool getInstance() {
        if (instance == null) {
            synchronized (DatabaseConnectionPool.class) {
                if (instance == null) {
                    instance = new DatabaseConnectionPool();
                }
            }
        }
        return instance;
    }
    
    public Connection getConnection() {
        return connection;
    }
    
    private Connection createConnection() {
        // 模拟创建数据库连接
        return new Connection() {
            @Override
            public Statement createStatement() throws SQLException {
                return null;
            }
            
            @Override
            public PreparedStatement prepareStatement(String sql) throws SQLException {
                return null;
            }
            
            @Override
            public CallableStatement prepareCall(String sql) throws SQLException {
                return null;
            }
            
            // 其他Connection接口方法的实现...
            
            @Override
            public void close() throws SQLException {
                System.out.println("关闭数据库连接");
            }
        };
    }
    
    public void closeConnection() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 获取单例实例
        DatabaseConnectionPool pool1 = DatabaseConnectionPool.getInstance();
        DatabaseConnectionPool pool2 = DatabaseConnectionPool.getInstance();
        
        // 验证是否是同一个实例
        System.out.println("pool1 == pool2: " + (pool1 == pool2)); // true
        
        // 使用连接
        Connection conn = pool1.getConnection();
        // 执行数据库操作...
        
        // 关闭连接
        pool1.closeConnection();
    }
}

优缺点

优点

  1. 内存中只有一个实例,减少了内存开支,尤其是频繁创建和销毁实例
  2. 避免对资源的多重占用
  3. 提供了对唯一实例的受控访问

缺点

  1. 违反了单一职责原则,单例类既充当了工厂角色又充当了产品角色
  2. 难以进行单元测试
  3. 在某些情况下可能造成内存泄漏

相关模式

  • 抽象工厂模式:可以使用单例模式作为工厂
  • 建造者模式:建造者模式可以创建单例对象
  • 原型模式:原型模式可以克隆单例对象

实际应用

  • Java中的Runtime类
  • Spring框架中的Bean默认是单例
  • 数据库连接池
  • 线程池
  • 缓存系统
  • 日志对象
  • 对话框
  • 注册表设置
相关推荐
木井巳1 天前
【多线程】单例模式
java·单例模式·java-ee
忧郁的Mr.Li1 天前
设计模式--单例模式
javascript·单例模式·设计模式
卷卷的小趴菜学编程1 天前
项目篇----仿tcmalloc的内存池设计(page cache)
c++·缓存·单例模式·tcmalloc·内存池·span cache
萧曵 丶2 天前
懒加载单例模式中DCL方式和原理解析
java·开发语言·单例模式·dcl
萧曵 丶2 天前
单例模式 7 种实现方式对比表
java·单例模式
当战神遇到编程6 天前
图书管理系统
java·开发语言·单例模式
Remember_9936 天前
Java 单例模式深度解析:设计原理、实现范式与企业级应用场景
java·开发语言·javascript·单例模式·ecmascript
春日见7 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
短剑重铸之日7 天前
《设计模式》第二篇:单例模式
java·单例模式·设计模式·懒汉式·恶汉式
钦拆大仁9 天前
Java设计模式-单例模式
java·单例模式·设计模式