单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

Implementation

懒汉式

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
package cn.leithda.single;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:09
* Description: 单例模式1,懒汉式,线程不安全,Lazy加载
*/
public class Singleton {
private static Singleton instance;

/**
* 设置构造函数为私有,防止类被实例化
*/
private Singleton(){}
/* 可以通过下面代码避免反射多次实例化 单例对象
private Singleton(){
if(instance != null){
throw new IllegalArgumentException("单例构造器不能重复使用");
}
}
*/

public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}

懒汉式2

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
package cn.leithda.single;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:20
* Description: 单例模式2,懒汉式,线程安全,Lazy加载,性能低
*/
public class Singleton2 {
private static Singleton2 instance;

private Singleton2() {
}

/**
* 加锁 synchronized 保证单例,影响效率
*/
public static synchronized Singleton2 getInstance() {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package cn.leithda.single;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:20
* Description: 单例模式3,饿汉式,线程安全,无Lazy加载,浪费内存
*/
public class Singleton3 {
/**
* 类装载时对象就会实例化,浪费内存
*/
private static Singleton3 instance = new Singleton3();

private Singleton3(){}

public static Singleton3 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
package cn.leithda.single;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:21
* Description: 单例模式4,双重锁验锁(DCL,即 double-checked locking),线程安全,Lazy加载,性能较高,实现复杂
*/
public class Singleton4 {
/**
* 增加volatile是为了避免代码优化时进行重排序,导致返回未初始化成功的对象
*/
private volatile static Singleton4 instance;

private Singleton4(){}

public static Singleton4 getInstance(){
// 当对象没有加载时,拿锁进行对象实例化保证单例。对象加载时,直接返回,性能较好
if(instance == null){
synchronized (Singleton4.class){
if(instance == null){
instance = new Singleton4();
}
}
}
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
package cn.leithda.single;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:21
* Description: 单例模式5,内部类实现,线程安全,Lazy加载
*/
public class Singleton5 {
/**
* 优化了Singleton3,使用静态内部类实现了Lazy加载
*/
private static class SingleHolder{
private static final Singleton5 INSTANCE = new Singleton5();
}
private Singleton5(){}

public static Singleton5 getInstance(){
return SingleHolder.INSTANCE;
}
}

枚举方式

1
2
3
4
5
6
7
8
9
10
11
12
13
package cn.leithda.single;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:22
* Description: 单例模式6
*/
public enum Singleton6 {
// 这种方式可以防止通过 reflection attact 调用类的私有构造方法
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
31
32
33
34
35
36
37
38
39
40
41
42
43
package cn.leithda.single;

import org.junit.Test;

/**
* Created with IntelliJ IDEA.
* User: leithda
* Date: 2021/5/27
* Time: 13:10
* Description: 单例模式测试
*/
public class SingleTest {


@Test
public void test() {
Singleton singletonFirst = Singleton.getInstance();
Singleton singletonSecond = Singleton.getInstance();
System.out.println("singleton==singleton1 is " + (singletonFirst == singletonSecond));

Singleton2 singleton2First = Singleton2.getInstance();
Singleton2 singleton2Second = Singleton2.getInstance();
System.out.println("singleton2First==singleton2Second is " + (singleton2First == singleton2Second));


Singleton3 singleton3First = Singleton3.getInstance();
Singleton3 singleton3Second = Singleton3.getInstance();
System.out.println("singleton3First==singleton3Second is " + (singleton3First == singleton3Second));


Singleton4 singleton4First = Singleton4.getInstance();
Singleton4 singleton4Second = Singleton4.getInstance();
System.out.println("singleton4First==singleton4Second is " + (singleton4First == singleton4Second));

Singleton5 singleton5First = Singleton5.getInstance();
Singleton5 singleton5Second = Singleton5.getInstance();
System.out.println("singleton5First==singleton5Second is " + (singleton5First == singleton5Second));

Singleton6 singleton6First = Singleton6.INSTANCE;
Singleton6 singleton6Second = Singleton6.INSTANCE;
System.out.println("singleton6First==singleton6Second is " + (singleton6First == singleton6Second));
}
}
  • 关于反射调用示例私有构造方法的例子可以自行测试,可以通过Singleton中方式避免反射执行私有构造函数。

Usage

  • 某些情况下,某个类要求只有一个实例的情况下
  • 对象需要被共享时,使用单例可以节约内存空间,加快访问速度。如Web中的配置对象、数据库连接池等
  • 当某个类需要频繁实例化,而创建的对象有频繁被销毁的情况下。如多线程的线程池、网络连接池等
  • 各种 配置类 XxxManager 及 工厂类 XxxFactory