Hashub

醉后不知天在水,满船清梦压星河

0%

单例模式

单例模式(Singleton)

1
2
确保一个类只有一个实例。
使用一个私有构造函数、一个私用静态变量、一个公有静态函数来实现
  • 私有构造函数保证了不能通过构造函数来创建实例,只能通过公有静态函数来返回唯一的私有静态变量

1. 懒汉式-线程不安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {

private static Singleton uniqueInstance;

private Singleton (){
}

public static Singleton getuniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
  • 以上代码是线程不安全的懒汉式单例。私有静态变量uniqueInstance被延迟实例化,如果没用到该类,那么就不会实例化该私有静态变量。从而节约资源。
  • 但是再多线程下,多个线程同时进入,遇到if判断为null。那么多个线程就会创建多个私有静态变量。导致实例化多次uniqueInstance。

2. 懒汉式-线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {

private static synchronized Singleton uniqueInstance;

private Singleton (){
}
public static Singleton getuniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}

  • 要使懒汉式线程安全只要再getuniqueInstance()方法加锁,保证一个时间点只有一个线程能够进去该方法。避免多次实例化该变量。
  • ==但是当一个线程进入该方法后,之后所有的线程都必须等待,这会使线程阻塞时间过长,影响性能。==

3. 饿汉式-线程安全

1
private static Singleton uniqueInstance = new Singleton();
  • ==线程安全的饿汉式采取直接实例化,但是丢失了延迟实例化带来的节约资源的好处。==

4. 双重校验锁-线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Singleton {

private volatile static Singleton uniqueInstance;

private Singleton (){
}

public static Singleton getuniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if(uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
  • 在第二点懒汉式-线程安全的基础上我们只有让私有静态变量实例化一次就可以使用,那么加锁操作只需要对实例化那部分进行加锁操作即可。即只有当私有静态变量没有被实例化的时候才需要加锁。
  • 第一个if判断是否已经存在私有变量实例,在两个线程都进入第一个判断语句。线程阻塞,在第一个进入第二个if判断之后实例化私有静态变量,在第二个线程进来之后判断语句不为空就不会多次实例化私有静态变量。

==volatile关键字==

在上面的代码中uniqueInstance用volatile关键修饰。为什么?

uniqueInstance = new Singleton();这段代码有三个执行步骤

  1. 为uniqueInstance分配内存空间
  2. 初始化uniqueInstance
  3. 将uniqueInstance指向分配的内存地址

以上这三步在JVM不是按序执行的,这是因为JVM有指令重排的特性,在单线程下没有问题,但是在多线程下,T1线程未初始化变量,T2调用getuniqueInstance()则判断uniqueInstance不为空,此时uniqueInstance并未初始化。因此需要使用volatile关键字禁止JVM的指令重排,保证多线程下能正常运行。

5. 静态内部类(懒汉式)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {

private Singleton() {
}

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

public static Singleton getUniqueInstance() {
return SingletonHolder.INSTANCE;
}
}
  • 当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用getUniqueInstance()方法时触发 SingletonHolder.INSTANCE SingletonHolder才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
  • ==这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。==

6. 枚举实现(饿汉式最简洁)

1
2
3
public enum SingletonDemo {
INSTANCE;
}

枚举实现单例模式