1.为什么要用单例化
确保只有一个实例:单例模式确保在整个应用程序中只存在一个实例。这对于某些对象来说是非常重要的,例如数据库连接、线程池、日志记录器等。通过限制实例数量,可以避免资源浪费和冲突。
比如数据库连接的建立非常耗时,多次的数据库连接实例化会严重拖慢程序运行速度。在斗地主大作业中,每次数据库的操作都会建立一次连接并关闭一次链接,造成了严重的延迟。
当涉及到数据库连接时,单例模式可以确保只有一个数据库连接实例存在,以便在整个应用程序中共享和重复使用该连接。下面是一个简单的数据库连接使用单例模式的例子:
public class DatabaseConnection {
private static DatabaseConnection instance;
private Connection connection;
private DatabaseConnection() {
// 私有化构造函数,防止外部实例化
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
} catch (SQLException e) {
e.printStackTrace();
}
}
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public Connection getConnection() {
return connection;
}
// 其他数据库操作方法
// ...
}
在上述示例中,DatabaseConnection类使用单例模式来提供数据库连接实例。通过私有化构造函数,防止外部直接实例化对象。getInstance()方法负责创建或返回单例实例,确保在整个应用程序中只有一个实例存在。
通过调用getConnection()方法,可以获取数据库连接实例,从而进行数据库操作。由于单例模式的特性,多个类可以通过DatabaseConnection.getInstance().getConnection()来获取相同的数据库连接实例。
这样做的好处是避免了多个地方创建和管理数据库连接,确保了连接的唯一性和共享性。这对于需要频繁进行数据库操作的应用程序来说,可以提高性能并减少资源消耗。
需要注意的是,上述示例并没有考虑多线程环境下的线程安全性。在实际应用中,可能需要采取额外的措施来保证在多线程环境下的线程安全访问,例如使用双重检查锁定或静态内部类方式。
提供全局访问点:
全局访问点:单例模式提供了一个全局访问点,使得其他类可以方便地访问该实例。这消除了对全局变量的需要,避免了在各处传递对象的麻烦。
资源共享和数据共享:
资源共享和数据共享:由于单例模式只有一个实例,可以更有效地管理共享的资源和数据。多个对象可以共享相同的实例,避免了重复创建和维护多个相同的对象。
延迟实例化:
延迟实例化:单例模式允许在需要时才创建实例,延迟了对象的实例化过程。这对于某些资源密集型或初始化耗时的对象来说是非常有用的,可以提高应用程序的性能和启动速度。
实现单一职责原则:
实现单一职责原则:单例模式对于某些类来说,确保只有一个实例存在有助于遵循单一职责原则。它将创建和管理实例的责任集中在一个地方,使得对象的职责更加清晰明确。
尽管单例模式具有这些优点,但也需要谨慎使用。过度使用单例模式可能导致代码的耦合性增加,增加了对象之间的依赖关系。因此,应根据具体的需求和场景判断是否使用单例模式,并权衡其利弊。
2.怎么使用单例化模式
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有化构造函数,防止外部实例化
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
// 其他方法
public void doSomething() {
// 执行操作
}
}
在上面的示例中,Singleton类的构造函数被私有化,这意味着其他类无法直接实例化Singleton对象。然后,定义了一个静态方法getInstance(),该方法检查类的静态变量instance是否为null。如果instance为null,则创建一个新的Singleton实例;如果instance已经存在,则直接返回现有的实例。通过这种方式,无论在何处调用getInstance()方法,都将始终返回相同的Singleton实例,从而确保只有一个实例存在。
客户端可以通过以下方式来使用单例模式:
Singleton singleton = Singleton.getInstance();
singleton.doSomething();
单例模式存在的问题及解决办法
线程不安全
多线程安全性:在多线程环境下使用单例模式时,需要注意实现线程安全,当多个线程同时调用getInstance()方法并且instance为null时,可能会创建多个实例,违背了单例模式的初衷。
解决方式
1.使用同步锁
使用同步锁(synchronized):在getInstance()方法上使用synchronized关键字,确保在同一时间只有一个线程可以访问该方法。这样可以避免多个线程同时创建实例,但会引入性能的开销。
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
2.使用双重检查锁定
双重检查锁定(Double-Checked Locking):在synchronized块内部进行双重检查。
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
只有当instance为null时才进行同步操作创建实例。避免了每次访问实例都进行同步的开销,因此在实例已经存在的环境下,后续的线程可以直接返回实例,而无需进入同步块。
3.使用静态内部类
使用静态内部类延迟加载实例的特性,并保证线程安全。当第一次调用getInstance()方法时,才会加载内部类并创建实例。
public class Singleton {
private Singleton() {
// 私有化构造函数
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
序列化和反序列化
序列化和反序列化:如果单例类需要支持序列化和反序列化,需要特殊处理,以确保反序列化后仍然是单例实例。(暂未学习)
单一职责原则
单一职责原则:单例模式常常被滥用,过度使用单例模式可能导致代码的耦合性增加,违反单一职责原则。
文章介绍了单例模式在确保资源管理和数据库连接一致性中的应用,强调了线程安全问题及其解决方法,如同步锁、双重检查锁定和静态内部类。此外,还提及了序列化和反序列化的注意事项以及单一职责原则的讨论。
2340





