设计模式源码git地址:design-pattern-src: 设计模式源码 (gitee.com)

  1. 什么是单例模式
  2. 使用场景
  3. 常见的应用场景
  4. 实现套路
  5. 八大实现方式

什么是单例模式

单例模式指某个类中只存在一个对象实例,创建对象的,属于创建者模式

使用场景

对于一些频繁创建销毁的对象来说,创建对象耗时或者消耗资源多,工具类对象

常见的应用场景

①Runtime类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Runtime {
private static Runtime currentRuntime = new Runtime();

/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}

/** Don't let anyone else instantiate this class */
private Runtime() {}
//省略
}

②数据库连接池

数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗

③spring的对象管理默认为单例模式

实现套路

虽然实现方式众多,但核心绕不开两点

1、私有化构造方法,防止外部类去new

2、静态变量定义,保证唯一性

八大实现方式

饿汉两种,懒汉三种,双重检查,静态内部,枚举

饿汉可以理解为:刚加载类就迫不及待创建对象,急

懒汉可以理解为:只有需要的时候才创建对象,不急

1、饿汉式(静态常量)

1
2
3
4
5
6
7
8
9
//外部不能new
private EHan1() {}
//创建一个对象实例
private final static EHan1 instance = new EHan1();

//对外提供接口
public static EHan1 getInstance() {
return instance;
}

优点:简单,类装载时加载,避免线程同步问题

缺点:没有达到懒加载,如果没用到,会造成内存浪费

2、饿汉式(静态代码块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//代码块创建
static {
instance = new EHan2();
}

//外部不能new
private EHan2() {

}
//创建一个对象实例
private static EHan2 instance;

//对外提供接口
public static EHan2 getInstance() {
return instance;
}

优缺点相同

3、懒汉式(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
//防外部new
private LanHan1() {}
//静态定义
private static LanHan1 instance;
//返回对象时获取
public static LanHan1 getInstance() {
//如果为空则创建
if (instance == null) {
instance = new LanHan1();
}
return instance;
}

测试方法相同

优点:简单,起到了懒加载效果,使用对象时才创建,

缺点:线程不安全(只能在单线程下使用),实际开发时不建议

4、懒汉式(线程安全,同步方法)

1
2
3
4
5
6
7
8
9
10
11
12
//防外部new
private LanHan2() {}
//静态定义
private static LanHan2 instance;
//返回对象时获取
public static synchronized LanHan2 getInstance() {
//如果为空则创建
if (instance == null) {
instance = new LanHan2();
}
return instance;
}

缺点:每个线程想要获取类的时候,都需要进行getInstance()方法获取,等待时间过长,效率低下,实际开发不建议使用这种方式

5、懒汉式(线程安全,同步代码块)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//防外部new
private LanHan3() {}
//静态定义
private static LanHan3 instance;
//返回对象时获取
public static LanHan3 getInstance() {
//如果为空则创建
if (instance == null) {
synchronized (LanHan3.class) {
instance = new LanHan3();
}
}
return instance;
}

缺点:对于效率低的问题,放入了代码块中执行,实际上不能起到线程同步的问题,实际开发中不能使用

6、双重检查(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//如果有变化立马刷新,不会重排序
private static volatile DoubleCheck instance;

private DoubleCheck() {}

//两个判断,一个同步方法
public static DoubleCheck getInstance() {
//如果此时已经有实例,那么直接返回实例,保证效率
if (instance == null) {
synchronized (DoubleCheck.class) {
//假设两个线程AB,A此时走到了这行,B在synchronized的上行,但已经判断为空了,A在走完创建实例方法之后
//如果没有判断,那么B会再次创建一次实例,那么就会存在多线程多个实例的情况
if (instance == null) {
instance = new DoubleCheck();
}
}
}
return instance;
}

优点:懒加载,线程安全,保证效率。推荐使用

两个判断的意义:第一个保证效率,如果有实例那么直接返回

volatile:保证可见性,防止小概率指令重排造成的失败

第二个为了保证线程安全,不然可能会出现多线程多实例的情况

7、静态内部类(推荐)

1
2
3
4
5
6
7
8
9
private NeiBu() {}

private static class getInstanceClass {
private static final NeiBu INSTANCE = new NeiBu();
}

public static NeiBu getInstance() {
return getInstanceClass.INSTANCE;
}

JVM在装载类的时候,线程是安全的,使用了类装载机制

在加载NeiBu类时,内部类getInstanceClass不会被加载,只有在调用getInstance的时候才会进行加载

优点:懒加载,线程安全。推荐使用

8、枚举类(推荐)

1
2
3
4
5
6
enum instance {
INSTANCE;
public void say() {
System.out.println("ok");
}
}

测试代码

1
2
3
instance instance1 = singleton.instance.INSTANCE;
instance instance2 = singleton.instance.INSTANCE;
System.out.println(instance1 == instance2);

借助jdk5提出的枚举类来实现到单例模式,不仅能避免同步问题,还能防止反序列化创建对象。推荐使用