设计模式

设计模式是软件设计中反复出现问题的通用解决方案。它们是经过多次验证和应用的指导原则,旨在帮助软件开发人员解决特定类型的问题,提高代码的可维护性、可扩展性和可重用性。

创建型模式

关注对象的实例化过程,包括了如何实例化对象、隐藏对象的创建细节。

单例模式

单例模型确保一个类只能创建一个实例,该类负责创建自己的对象,并且保证只有单个对象被创建。单例类将自己的构造函数声明为私有,以防止外部代码之间创建实例。

  • 懒汉式:类加载是不会触发该单实例对象被创建,而是在首次使用该对象时候被创建

  • 饿汉式:类加载就会触发该单实例对象被创建

饿汉式-方式一(静态变量方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 饿汉式
* 静态变量创建类的对象
*/
public class Singleton {
//私有构造方法
private Singleton() {}

//在成员位置创建该类的对象
private static Singleton instance = new Singleton();

//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}

说明:

类初始化时JVM执行clinit指令从上至下执行静态代码赋值语句静态代码块,instance对象被创建。如果对象足够大而一直没有被使用就会造成内存资源的浪费。

饿汉式-方式二(静态代码块方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 饿汉式
* 在静态代码块中创建该类对象
*/
public class Singleton {

//私有构造方法
private Singleton() {}

//在成员位置创建该类的对象
private static Singleton instance;

static {
instance = new Singleton();
}

//对外提供静态方法获取该对象
public static Singleton getInstance() {
return instance;
}
}

说明:

与饿汉式方式一相同

懒汉式-方式一(线程不安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 懒汉式
* 线程不安全
*/
public class Singleton {
//私有构造方法
private Singleton() {}

//在成员位置创建该类的对象
private static Singleton instance;

//对外提供静态方法获取该对象
public static Singleton getInstance() {

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

说明:

类初始化不会进行instance对象的初始化,通过调用getInstance()静态方法获取Singleton类的对象时才会创建Singleton对象,实现了懒加载的效果。

线程不安全:

假设现在有两个线程t1和t2同时调用getInstance() 方法,t1刚判断instance == null为true进入内部,刚要进行创建,此时正好丢失CPU执行权,进入阻塞状态,而t2获得CPU执行权,也进行了instance == null判断,发现也是true,就进入了内部代码进行创建,此时我们发现t1和t2都会执行内部的new代码,从而获得了两个不同的实例对象。

懒汉式-方式二(线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 懒汉式
* 线程安全
*/
public class Singleton {
//私有构造方法
private Singleton() {}

//在成员位置创建该类的对象
private static Singleton instance;

//对外提供静态方法获取该对象
public static synchronized Singleton getInstance() {

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

说明:

对静态方法getInstance()加上了synchronized关键字修饰,导致该方法的执行效率低,而线程安全问题只发生在初始化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
/**
* 双重检查方式
*/
public class Singleton {

//私有构造方法
private Singleton() {}

private static Singleton instance;

//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为null
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

说明

双重检查锁是一种非常好的实例实现方式,解决了单例、性能、线程安全上的问题,但是存在多线程情况下的空指针问题。

1
2
3
4
5
6
7
8
9
10
11
// instance = new instance();
// 拆分为3步
memory=allocate();//1.分配对象的内存空间
ctorInstance(memory);//2.初始化对象
instance=memory;//3.设置instance指向刚分配的内存空间

// 在上面的代码里可会被JIT即时编译优化进行指令重排,变为下面的代码:
memory=allocate();//1.分配对象的内存空间
instance=memory;//2.设置instance指向刚分配的内存空间(此时对象并未完成初始化)
ctorInstance(memory);//3.初始化对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 双重检查方式
*/
public class Singleton {
//私有构造方法
private Singleton() {}
//volatile关键字修饰禁止指令重排、保证可见性、不保证原子性
private static volatile Singleton instance;
//对外提供静态方法获取该对象
public static Singleton getInstance() {
//第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实际
if(instance == null) {
synchronized (Singleton.class) {
//抢到锁之后再次判断是否为空
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

懒汉式-方式四(静态内部类方式

静态内部类单例模式中实例由内部类创建,由于 JVM 在加载外部类的过程中, 是不会加载静态内部类的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被 static 修饰,保证只被实例化一次,并且严格保证实例化顺序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 静态内部类方式
*/
public class Singleton {
//私有构造方法
private Singleton() {}

private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}

//对外提供静态方法获取该对象
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

说明:

第一次加载Singleton类时不会去初始化INSTANCE,只有第一次调用getInstance,虚拟机加载SingletonHolder并初始化INSTANCE,这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。