网站整体运营思路,如何做卖菜网站,网站建设规划总结,WordPress更改数据库连接简介
单例模式是一种创建型设计模式#xff0c;确保某个类仅有一个实例#xff0c;并提供一个全局访问点来访问该实例。 在单例模式中#xff0c;类负责创建自己的对象#xff0c;同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式#xff0c;允许直接访…简介
单例模式是一种创建型设计模式确保某个类仅有一个实例并提供一个全局访问点来访问该实例。 在单例模式中类负责创建自己的对象同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象的方式允许直接访问而无需每次实例化该类的新对象。
主要场景
单例模式的主要应用场景包括
需要频繁实例化然后销毁的对象。创建对象时耗时过多或者耗资源过多但又经常用到的对象。有状态的工具类对象。频繁访问数据库或文件的对象。
通过应用单例模式我们可以控制实例的数量节省系统资源同时加快对象访问速度尤其适合对象需要被公用的场合例如多个模块使用同一个数据源连接对象等。
实现机制
实现单例模式的关键在于确保只有一个实例被创建并提供一个全局访问点。这通常通过以下方式实现
将构造函数设为私有以防止其他类通过new操作符创建该类的实例。在类内部创建一个静态的私有实例。提供一个静态的公有方法用于返回该类的唯一实例。如果该实例尚未创建则通过调用私有构造函数来创建它如果实例已经存在则直接返回该实例。
注意多线程
需要注意的是在多线程环境下需要确保单例模式的线程安全性以避免出现多个实例的情况。 单例模式确保线程安全的关键在于确保在多线程环境下类的唯一实例能够被正确地创建和访问而不会出现多个实例或者创建过程中的竞争条件。以下是一些常见的线程安全的单例模式实现方式
饿汉式静态初始化
这种方式是最简单的线程安全的实现方式。因为静态初始化器由JVM在加载类时执行且JVM保证类的加载过程是线程安全的所以在多线程环境下这种方式创建的实例也是线程安全的。
public class Singleton {// 在加载时就完成了初始化所以类加载较慢但获取对象的速度快private static Singleton instance new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}双重检查锁定Double-Checked Locking
双重检查锁定是一种优化技术旨在减少使用同步的开销。它首先检查实例是否已经存在如果不存在才进行同步。但是在Java中双重检查锁定需要正确地使用volatile关键字来确保可见性和禁止指令重排 public class Singleton { // volatile 保证多线程正确处理 instance 的可见性和禁止指令重排 private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; }
}在Java 5及以上版本中双重检查锁定是可行的但务必注意volatile关键字的正确使用。
静态内部类
静态内部类实现单例模式的方式也是线程安全的。由于JVM的类加载机制保证了静态内部类只会被加载一次从而保证了线程安全性。
public class Singleton {private Singleton() {}// 使用静态内部类来创建单例也是线程安全的private static class SingletonHolder {private static final Singleton INSTANCE new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}枚举
在Java中枚举类型是线程安全的并且能防止反序列化重新创建新的对象所以使用枚举实现单例模式也是线程安全的最佳实践。
public enum Singleton {INSTANCE;public void whateverMethod() {// method body}
}通过枚举实现单例模式不仅简单而且自动支持序列化机制防止反序列化重新创建新的对象。
初始化时同步Initialization on Demand Holder, IODH
这种方式结合了饿汉式和双重检查锁定的思想但在实现上更为复杂且在某些JVM上可能并不是线程安全的。因此它不如上面提到的方法常用。
在选择单例模式的实现方式时应该考虑代码的简洁性、性能需求以及线程安全性。通常情况下使用枚举或静态内部类实现单例模式是比较推荐的方式因为它们不仅线程安全而且实现简单易于理解。
实际例子
日志系统
假设我们正在开发一个日志记录系统我们想要确保整个应用程序中只有一个日志记录器实例这样我们就可以保持日志的一致性和管理方便。
public class Logger {// 私有构造方法防止外部通过new创建实例private Logger() {// 初始化代码System.out.println(Logger is being initialized...);}// 静态内部类持有单例的引用private static class LoggerHolder {// 静态初始化器保证线程安全private static final Logger INSTANCE new Logger();}// 获取单例的公共静态方法public static Logger getInstance() {// 返回LoggerHolder中持有的单例引用return LoggerHolder.INSTANCE;}// 日志记录方法public void log(String message) {System.out.println([ System.currentTimeMillis() ] message);}// 示例使用单例模式的Logger类public static void main(String[] args) {// 获取Logger单例Logger logger Logger.getInstance();// 使用Logger实例记录日志logger.log(This is a log message.);// 尝试再次获取Logger实例应该是同一个Logger anotherLogger Logger.getInstance();// 验证两个引用是否指向同一对象System.out.println(Are loggers the same? (logger anotherLogger));}
}数据库连接池
我们手撸一个最简单的数据库连接池保证在并发情况下的单例访问。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.ConcurrentHashMap; public class ConnectionPoolManager { // 静态变量持有单例引用 private static ConnectionPoolManager instance; // 数据库连接池 private final ConcurrentHashMapString, Connection pool; // 初始化连接池的大小 private static final int MAX_CONNECTIONS 10; // 当前已分配的连接数 private int currentConnections 0; // 数据库连接信息 private static final String DB_URL jdbc:mysql://localhost:3306/mydb; private static final String DB_USER username; private static final String DB_PASSWORD password; // 私有构造方法防止外部通过new创建实例 private ConnectionPoolManager() { this.pool new ConcurrentHashMap(); // 初始化连接池预先创建一些连接 for (int i 0; i MAX_CONNECTIONS; i) { try { Connection connection DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); pool.put(connection i, connection); } catch (SQLException e) { e.printStackTrace(); } } } // 从连接池中获取一个连接 public synchronized Connection getConnection() { if (currentConnections MAX_CONNECTIONS) { // 尝试从连接池中获取一个连接 for (String key : pool.keySet()) { Connection connection pool.get(key); if (connection ! null !connection.isClosed()) { pool.remove(key); // 从连接池中移除表示该连接已被使用 currentConnections; return connection; } } // 如果连接池中没有可用连接则创建新连接此处简化处理实际应检查是否超过最大连接数 try { Connection connection DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); currentConnections; return connection; } catch (SQLException e) { e.printStackTrace(); } } return null; // 如果没有可用连接返回null } // 回收一个连接到连接池 public synchronized void releaseConnection(Connection connection) { if (connection ! null !connection.isClosed()) { pool.put(connection (MAX_CONNECTIONS - currentConnections), connection); currentConnections--; } } // 获取单例的公共静态方法使用双重检查锁定确保线程安全 public static ConnectionPoolManager getInstance() { if (instance null) { synchronized (ConnectionPoolManager.class) { if (instance null) { instance new ConnectionPoolManager(); } } } return instance; } // 示例使用单例模式的ConnectionPoolManager类 public static void main(String[] args) { // 获取连接池管理器单例 ConnectionPoolManager connectionPoolManager ConnectionPoolManager.getInstance(); // 从连接池获取一个连接 Connection connection connectionPoolManager.getConnection(); if (connection ! null) { // 使用连接执行数据库操作... System.out.println(Got a connection from the pool.); // 假设使用完连接后释放回连接池 connectionPoolManager.releaseConnection(connection); System.out.println(Released the connection back to the pool.); } else { System.out.println(No connections available in the pool.); } }
}在这个例子中ConnectionPoolManager 类负责管理一个数据库连接池。它使用了 ConcurrentHashMap 来存储连接以便能够高效地处理并发请求。getConnection 方法尝试从连接池中获取一个可用连接如果没有可用连接则尝试创建一个新连接这里简化了处理逻辑实际中可能需要考虑连接数是否已经达到最大值。releaseConnection 方法则将使用完毕的连接回收回连接池。