单例模式有两种常见的实现方式:饿汉式 和懒汉式。以下是它们的代码示例:
1. 饿汉式(Eager Initialization)
在类加载时就创建实例,线程安全,适用于单例对象耗费资源较小的场景。
java
public class SingletonEager {
// 在类加载时就创建实例
private static final SingletonEager instance = new SingletonEager();
// 私有构造方法,防止外部实例化
private SingletonEager() {}
// 提供全局访问点
public static SingletonEager getInstance() {
return instance;
}
}
优点:
- 简单直观
- 线程安全,不需要同步
缺点:
- 即使不使用该实例,类加载时也会创建对象,可能造成资源浪费。
2. 懒汉式(Lazy Initialization)
在第一次调用 getInstance()
时才创建实例,适用于单例对象较耗资源且不一定每次都使用的场景。
2.1 线程不安全的懒汉式:
java
public class SingletonLazy {
// 延迟初始化
private static SingletonLazy instance;
// 私有构造方法
private SingletonLazy() {}
// 提供全局访问点(线程不安全)
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
优点:
- 按需加载,节省资源
缺点:
- 线程不安全,多个线程可能同时创建多个实例
2.2 线程安全的懒汉式(同步方法):
java
public class SingletonLazyThreadSafe {
private static SingletonLazyThreadSafe instance;
private SingletonLazyThreadSafe() {}
// 通过同步锁保证线程安全
public static synchronized SingletonLazyThreadSafe getInstance() {
if (instance == null) {
instance = new SingletonLazyThreadSafe();
}
return instance;
}
}
优点:
- 线程安全,简单易懂
缺点:
- 每次获取实例都需要同步,性能开销较大
2.3 双重检查锁(Double-Check Locking):
java
public class SingletonLazyDoubleCheck {
private static volatile SingletonLazyDoubleCheck instance;
private SingletonLazyDoubleCheck() {}
public static SingletonLazyDoubleCheck getInstance() {
if (instance == null) {
synchronized (SingletonLazyDoubleCheck.class) {
if (instance == null) {
instance = new SingletonLazyDoubleCheck();
}
}
}
return instance;
}
}
优点:
- 线程安全,且只在第一次创建时进行同步,性能较好
缺点:
- 代码较复杂,需要注意
volatile
的使用
3. 静态内部类(推荐使用)
静态内部类的方式结合了懒汉式和饿汉式的优点,线程安全且高效。
java
public class SingletonStaticInnerClass {
private SingletonStaticInnerClass() {}
// 静态内部类
private static class SingletonHolder {
private static final SingletonStaticInnerClass INSTANCE = new SingletonStaticInnerClass();
}
public static SingletonStaticInnerClass getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:
- 利用了类加载机制,保证线程安全
- 延迟初始化,按需加载
- 不需要同步,性能较高
这几种方式都可以实现单例模式,具体使用时可以根据项目的实际需求进行选择。
在 JDK 中,单例模式被广泛应用于很多核心类和工具类中。以下是一些经典的 JDK 类或组件使用单例模式的例子:
jdk中使用到的单例模式
1. java.lang.Runtime
Runtime
类用于与 JVM 交互,并提供了管理和控制 JVM 的方法。它使用了饿汉式单例。
java
Runtime runtime = Runtime.getRuntime();
- 使用场景 :
Runtime
提供了 JVM 相关的全局服务,比如执行操作系统命令、获取可用的内存和处理器数量等。 - 实现 :通过
getRuntime()
方法获取唯一实例,类加载时就创建实例。
2. java.awt.Desktop
Desktop
类提供了一些平台相关的桌面操作功能,如打开浏览器、邮件客户端等。它也是通过单例模式实现的。
java
Desktop desktop = Desktop.getDesktop();
- 使用场景:用于与本地操作系统的桌面功能交互,如打开文件、发送邮件等。
- 实现 :通过
getDesktop()
方法获取唯一实例。
3. java.lang.System
System
类提供了许多静态方法,用于与系统环境交互,如读取环境变量、标准输入输出等。它虽然没有传统意义的 getInstance()
,但其设计本质上是一种单例模式,主要通过静态方法提供系统服务。
java
System.out.println("Hello, World!");
- 使用场景:处理标准输入输出流、系统属性、环境变量等。
- 实现:通过静态方法提供全局访问,不允许实例化。
4. java.util.logging.LogManager
LogManager
类负责管理全局日志配置,确保整个应用程序中日志记录的一致性。
java
LogManager logManager = LogManager.getLogManager();
- 使用场景:控制全局的日志配置,管理所有的 Logger 实例。
- 实现:使用单例模式确保只有一个日志管理器实例在运行时存在。
5. java.sql.DriverManager
DriverManager
用于管理数据库驱动程序,并处理与数据库的连接请求。
java
Connection connection = DriverManager.getConnection(url, user, password);
- 使用场景:管理数据库连接,加载和注册 JDBC 驱动程序。
- 实现:通过静态方法管理数据库连接,全局管理驱动程序的加载。
6. java.util.Calendar
Calendar
类用来操作日期和时间的实例,在一些实现(如 GregorianCalendar
)中也使用了单例模式。
java
Calendar calendar = Calendar.getInstance();
- 使用场景:用于日期和时间的计算与操作。
- 实现 :通过
getInstance()
提供全局访问点。
7. java.nio.file.FileSystems
FileSystems
类用来管理文件系统的访问。
java
FileSystem fileSystem = FileSystems.getDefault();
- 使用场景:获取文件系统的根路径,处理路径的文件操作。
- 实现 :通过
getDefault()
方法获取默认的文件系统,内部实现单例模式。
总结
这些 JDK 中的类,采用单例模式的原因通常是:
- 确保系统中某个类只有一个实例,避免资源浪费。
- 提供全局访问点,使得这些实例能够被多个地方访问和共享。
- 方便管理与外部系统交互的资源,如日志管理器、数据库连接、文件系统、JVM 资源等。