目录

[TOC]

什么是单例模式

单例设计模式(Singleton Pattern)是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这种模式在需要确保某个类只有一个实例的情况下非常有用,常用于管理共享资源或限制对象的创建。

单例模式应用场景

  1. 配置管理:在应用程序中,可能需要使用一些全局的配置信息,例如数据库连接字符串、日志级别等。这些配置信息通常只需要一份,因此可以使用单例模式来实现。通过单例模式,我们可以在应用程序启动时创建一个唯一的实例,并在整个应用程序中共享这个实例。这样可以避免多个线程或进程同时访问和修改配置信息,从而保证了配置信息的一致性和可靠性。
  2. 日志记录:在应用程序中,可能需要使用一个全局的日志记录器,用于记录应用程序的运行情况和错误信息。同样地,只需要一份日志记录器即可,因此可以使用单例模式来实现。通过单例模式,我们可以在应用程序启动时创建一个唯一的实例,并在整个应用程序中共享这个实例。这样可以避免多个线程或进程同时访问和修改日志记录器,从而保证了日志记录的一致性和可靠性。
  3. 缓存管理:在应用程序中,可能需要使用一个全局的缓存系统,用于缓存一些数据以提高应用程序的性能。同样地,只需要一份缓存系统即可,因此可以使用单例模式来实现。通过单例模式,我们可以在应用程序启动时创建一个唯一的实例,并在整个应用程序中共享这个实例。这样可以避免多个线程或进程同时访问和修改缓存系统,从而保证了缓存系统的一致性和可靠性。
  4. 资源管理:在应用程序中,可能需要使用一些全局的资源,例如文件句柄、网络连接等。这些资源通常只需要一份,因此可以使用单例模式来实现。通过单例模式,我们可以在应用程序启动时创建一个唯一的实例,并在整个应用程序中共享这个实例。这样可以避免多个线程或进程同时访问和修改资源,从而保证了资源的一致性和可靠性。

代码

懒汉式单例模式(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 懒汉式单例模式(线程不安全)
*/
public class LazySingleton {
/**
* 创建一个私有静态变量instance,用于存储单例对象
*/
private static LazySingleton instance;

/**
* 将构造方法设为私有,防止外部创建新的实例
*/
private LazySingleton() {
}

// 获取单例实例的方法
public static LazySingleton getInstance() {
// 如果实例还未创建,创建一个新实例返回
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}

懒汉式单例模式(线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 懒汉式单例模式(线程安全)
*/
public class SafeLazySingleton {
/**
* 创建一个私有静态变量instance,用于存储单例对象
*/
private static SafeLazySingleton instance;

/**
* 将构造方法设为私有,防止外部创建新的实例
*/
private SafeLazySingleton() {
}

/**
* 获取单例实例的方法,使用 synchronized 修饰以保证线程安全
*/
public static synchronized SafeLazySingleton getInstance() {
// 如果实例还未创建,创建一个新实例返回
if (instance == null) {
instance = new SafeLazySingleton();
}
return instance;
}
}

饿汉式单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 饿汉式单例模式
*/
public class HungrySingleton {
/**
* 类加载时就创建单例实例,使用 final 修饰以保证实例唯一性
*/
private static final HungrySingleton instance = new HungrySingleton();

/**
* 将构造方法设为私有,防止外部创建新的实例
*/
private HungrySingleton() {
}

/**
* 获取单例实例的方法,直接返回预先创建的实例
*/
public static HungrySingleton getInstance() {
return instance;
}
}

双重检查锁单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 双重检查锁单例模式
*/
public class DoubleCheckSingleton {
/**
* 检查实例是否已创建的 volatile 变量,可见性保证线程安全
*/
private static volatile DoubleCheckSingleton instance;

/**
* 将构造方法设为私有,防止外部创建新的实例
*/
private DoubleCheckSingleton() {
}

/**
* 获取单例实例的方法,使用双重检查锁保证线程安全和性能
*/
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
// 如果实例还未创建,创建一个新实例返回
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}

静态内部类单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 静态内部类单例模式
*/
public class StaticInnerClassSingleton {
/**
* 私有构造函数,避免类在外部被实例化
*/
private StaticInnerClassSingleton() {
}

;

/**
* 静态内部类持有单例实例,类加载时不会进行初始化。
* 直到第一次获取实例时才创建,并确保实例只被初始化一次。
*/
private static class Singleton {
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}

/**
* 返回唯一实例的方法,使用静态内部类中的INSTANCE进行实例的获取。
*/
public static StaticInnerClassSingleton getInstance() {
return Singleton.INSTANCE;
}

}

枚举单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 枚举单例模式
*/
public enum EnumSingleton {
INSTANCE;
int value;

/**
* 自定义构造函数
*/
private EnumSingleton() {
value = 1;
System.out.println("INSTANCE now created!");
}

public int getValue() {
return value;
}

public void setValue(int value) {
this.value = value;
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.xiaofei.singleton;

import org.junit.Test;

/**
* 单例模式测试
*/
public class SingletonTest {

@Test
public void lazySingletonTest() {
System.out.println("懒汉式单例模式(线程不安全)");
LazySingleton instance = LazySingleton.getInstance();
System.out.println(instance);
System.out.println("懒汉式单例模式(线程不安全)\n");

}

@Test
public void safeLazySingleton() {
System.out.println("懒汉式单例模式(线程安全)");
SafeLazySingleton instance = SafeLazySingleton.getInstance();
System.out.println(instance);
System.out.println("懒汉式单例模式(线程安全)\n");
}

@Test
public void hungrySingleton() {
System.out.println("饿汉式单例模式");
HungrySingleton instance = HungrySingleton.getInstance();
System.out.println(instance);
System.out.println("饿汉式单例模式\n");
}

@Test
public void doubleCheckSingleton() {
System.out.println("双重检查锁单例模式");
DoubleCheckSingleton instance = DoubleCheckSingleton.getInstance();
System.out.println(instance);
System.out.println("双重检查锁单例模式\n");
}

@Test
public void staticInnerClassSingleton() {
System.out.println("静态内部类单例模式");
StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
System.out.println(instance);
System.out.println("静态内部类单例模式\n");
}

@Test
public void enumSingleton() {
System.out.println("枚举单例模式");
EnumSingleton singleton = EnumSingleton.INSTANCE;
System.out.println(singleton.getValue());
singleton.setValue(2);
System.out.println(singleton.getValue());
System.out.println("枚举单例模式\n");

}
}

image-20230607151223998

总结

  • 懒汉式单例模式(线程不安全):
    • 优点:实现简单,性能好。
    • 缺点:线程不安全,需要在多线程环境下进行同步处理。
  • 懒汉式单例模式(线程安全):
    • 优点:线程安全,不需要额外的同步处理。
    • 缺点:实现相对复杂,性能略差于饿汉式单例模式。
  • 饿汉式单例模式:
    • 优点:线程安全,性能好。
    • 缺点:创建实例时会耗费一定的时间和资源。
  • 双重检查锁单例模:
    • 优点:线程安全,性能好。
    • 缺点:实现相对复杂。
  • 静态内部类单例模式:
    • 优点:线程安全,性能好。
    • 缺点:实现相对复杂。
  • 枚举单例模式:
    • 优点:线程安全,性能好。
    • 缺点:枚举类型本身不能被修改,因此如果需要扩展功能则需要修改枚举类型。

综上所述,不同的单例模式适用于不同的场景。根据实际的需求选择合适的单例模式可以提高程序的效率,并保证单例实例的唯一性。