设计模式-单例模式(Singleton)
设计模式文档无水印可复制
一、单例模式概述
1.1 什么是单例模式
Java 单例模式(Singleton)是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于需要频繁创建和销毁的对象,例如数据库连接、线程池等。
1.2、实现 Java 单例模式的步骤
- 1、将类的构造方法设置为私有,以防止外部直接创建实例。
- 2、在类中创建一个静态私有成员变量,用于存储唯一的实例。
- 3、提供一个公共的静态方法,用于获取唯一的实例。在这个方法中,首先检查实例是否已经存在,如果不存在,则创建一个新的实例并将其赋值给静态成员变量;如果已经存在,则直接返回该实例。
- 4、为了确保线程安全,可以使用双重检查锁定(Double-Checked Locking)机制。在获取实例的方法中,首先检查实例是否已经创建,如果没有创建,才进入同步代码块。这样可以降低锁的开销,提高性能。
以下是一个简单的 Java 单例模式实现:
public class Singleton {
// 静态私有成员变量,用于存储唯一的实例
private static Singleton instance;
// 将构造方法设置为私有,以防止外部直接创建实例
private Singleton() {
}
// 提供一个公共的静态方法,用于获取唯一的实例
public static Singleton getInstance() {
// 第一次检查:如果实例尚未创建,才进入同步代码块
if (instance == null) {
synchronized (Singleton.class) {
// 第二次检查:确保在当前线程进入同步代码块之前,其他线程没有创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
1.3 使用单例模式注意事项
- 1、私有构造函数:确保类的构造函数是私有的,以防止外部实例化。
- 2、静态成员变量:在类中定义一个静态成员变量,用于存储唯一的实例。
- 3、获取实例的方法:提供一个公共静态方法,用于获取唯一的实例。在这个方法中,需要判断实例是否已经创建,如果没有创建,则创建一个新的实例并返回;如果已经创建,则直接返回已创建的实例。
- 4、线程安全:如果需要在多线程环境下使用单例模式,需要考虑线程安全问题。可以使用双重检查锁定(Double-Checked Locking)或者静态内部类的方式来实现线程安全的单例模式。
- 5、延迟加载:为了提高性能,可以将实例的创建放在第一次访问时进行,而不是在类加载时就创建。这样可以节省资源,特别是在实例创建成本较高的情况下。
二、 单例模式的用途
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
以下是一些主要的应用场景:
- 1、整个程序的运行中只允许有一个类的实例。这是单例模式最典型的应用场景。
- 2、需要频繁实例化然后销毁的对象。例如,数据库连接、线程池等。在这种情况下,使用单例模式可以降低系统资源消耗,因为只需要创建一个实例,避免了反复创建和销毁对象的性能开销。
- 3、创建的对象需要占用大量的系统资源,例如 I/O 流、网络连接等。为了保证资源在任何时刻都能被共享,可以使用单例模式来实现对象的全局访问。
- 4、当类需要维护某些状态时,也可以使用单例模式。因为单例模式保证只有一个实例,所以可以确保状态在全局范围内保持一致。
三、单例模式的实现方式
单例模式的实现方式有多种,以下是一些常见的实现方式:饿汉式、懒汉式、双重校验(Dual-Check)、静态内部类、枚举;
3.1 饿汉式
这种实现方式在类被加载时就完成了实例化,所以类加载比较慢,但获取对象的速度快。这种方式基于类加载机制避免了多线程的同步问题,不过,它不能实现延时加载,也没有用到同步技术。
以下是使用 Java 饿汉模式实现单例模式的示例代码:
public class Singleton {
// 创建一个私有静态成员变量,用于存储单例对象
private static final Singleton instance = new Singleton();
// 将构造方法设为私有,防止外部直接创建对象
private Singleton() {}
// 提供一个公共静态方法,用于获取单例对象
public static Singleton getInstance() {
return instance;
}
}
在上述代码中,我们定义了一个名为 Singleton 的类,并在其中创建了一个私有静态成员变量 instance,用于存储单例对象。然后,我们将构造方法设为私有,以防止外部直接创建对象。最后,我们提供了一个公共静态方法 getInstance(),用于获取单例对象。当调用该方法时,会直接返回已经创建好的单例对象。
3.2 懒汉式
该方式是当第一次调用 getInstance() 方法时实例化对象。这种方式相比于饿汉式,节省了系统资源,但第一次获取对象时需要加锁,会影响性能。
以下是使用 Java 实现懒汉式单例模式的示例代码:
public class Singleton {
// 创建一个私有静态成员变量,用于存储单例对象
private static Singleton instance;
// 将构造方法设为私有,防止外部直接创建对象
private Singleton() {}
// 提供一个公共静态方法,用于获取单例对象
public static synchronized Singleton getInstance() {
// 判断实例是否已经创建,如果未创建则创建并返回
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在上述代码中,我们首先定义了一个私有静态成员变量 instance,用于存储单例对象。然后,我们将构造方法设为私有,以防止外部直接创建对象。接下来,我们提供了一个公共静态方法 getInstance(),用于获取单例对象。在该方法中,我们首先判断实例是否已经创建,如果未创建则创建并返回。由于使用了 synchronized 关键字,因此这个方法是线程安全的。
3.3 双重校验(Dual-Check)
这种方式既能保证线程安全又能保证延迟加载。在多线程环境中仍能保持高性能。
双重校验实现单例模式的代码如下:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
3.4 静态内部类
这种方式兼具延迟初始化和线程安全性。这种方式采用了静态内部类的特性来实现单例,不仅能保证线程安全,而且能够支持懒加载。
以下是一个示例:
public class Singleton {
// 创建一个私有静态内部类
private static class SingletonHolder {
// 创建一个私有静态实例
private static final Singleton INSTANCE = new Singleton();
}
// 将构造方法设为私有,防止外部实例化
private Singleton() {}
// 提供一个公共静态方法,用于获取单例实例
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
使用这个单例类的方式如下:
public class Main {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2); // 输出 true,表示两个实例是同一个对象
}
}
3.5 枚举
这种方式通过 Java 枚举类型本身的特性来保证了实例创建的单一性,同时枚举类型的实例是在类加载时就进行了初始化的,所以对资源的利用率更高。
以下是一个简单的示例:
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Doing something...");
}
}
在这个示例中,Singleton是一个枚举类型,它只有一个实例INSTANCE。你可以通过Singleton.INSTANCE来访问这个实例,并调用其方法。例如:
public class Main {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
这段代码会输出"Doing something…"。