概述
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。
意图
- 确保一个类只有一个实例
- 提供一个全局访问点来访问这个实例
适用场景
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时
结构
┌─────────────┐
│ 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();
}
}
优缺点
优点
- 内存中只有一个实例,减少了内存开支,尤其是频繁创建和销毁实例
- 避免对资源的多重占用
- 提供了对唯一实例的受控访问
缺点
- 违反了单一职责原则,单例类既充当了工厂角色又充当了产品角色
- 难以进行单元测试
- 在某些情况下可能造成内存泄漏
相关模式
- 抽象工厂模式:可以使用单例模式作为工厂
- 建造者模式:建造者模式可以创建单例对象
- 原型模式:原型模式可以克隆单例对象
实际应用
- Java中的Runtime类
- Spring框架中的Bean默认是单例
- 数据库连接池
- 线程池
- 缓存系统
- 日志对象
- 对话框
- 注册表设置