单例
懒汉式(非线程安全)
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 Singleton implements Serializable {
/**
* 核心为静态变量
*/
private static Singleton instance;
/**
* 私有构造方法
*/
private Singleton() {
}
/**
* 工厂方法
* @return
*/
private static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
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 Singleton implements Serializable {
/**
* 核心为静态变量
* 加volatile确保异步可见性
*/
private static volatile Singleton instance;
/**
* 私有构造方法
*/
private Singleton() {
}
/**
* 工厂方法
* 加锁确保线程安全
* @return
*/
private static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
|
这种方式把synchornized
加在工厂方法上,导致每次调用工厂方法都会阻塞,从而效率低下。
双重锁(DLC),线程安全
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| /**
* 双重锁单例
* 线程安全
*/
public class DlcSingleton {
private static volatile DlcSingleton instance;
private DlcSingleton() {
}
public static DlcSingleton getInstance() {
if (instance == null) {// 避免阻塞,提高效率
synchronized (DlcSingleton.class) {
if (instance == null) {//防止二次创建
instance = new DlcSingleton();
}
}
}
return instance;
}
}
|
双重锁的特点就是在工厂方法中加入了同步代码块和双重if判断,使得线程安全。容易忘掉的是静态变量添加的volatile
关键字。
考点:
- synchronized类锁,提供了原子性,是为了线程安全引入,避免多个线程调用工厂方法时,由于信息不同步导致重复创建。
- volatile关键字,提供了可见性,由于加锁只能保证原子性,并不保证可见性,所以在并发场景下,一个线程创建了实例,但是对另一个线程不可见,导致另一个线程得到null,所以加volatile读写屏障以保证线程可见性。
- 同步块里面的if判空,用意与普通的单例中的if判断是一致的,是为了避免二次创建。
- 同步块外面的if判空,在前面说过,线程安全的单例模式的弊病就是每次调用工厂方法都会同步阻塞,降低了效率。在这里加入if判空,就是为了如果实例已经创建,那么就不会进入同步代码块,从而提高了效率。
静态内部类懒汉式(线程安全)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| /**
* 静态内部类懒汉式
*/
public class InnerSingleton {
/**
* 私有构造方法
*/
private InnerSingleton(){}
/**
* 静态内部类
*/
private static class InnerSingletonHolder{
private static final InnerSingleton INSTANCE = new InnerSingleton();
}
public static final InnerSingleton getInstance(){
return InnerSingletonHolder.INSTANCE;
}
}
|
主要是靠静态内部类持有静态实例。
初看上去写法很饿汉式很像,但是在使用时,加载InnerSingleton
类型时,并不会同时加载其静态内部类,只有调用工厂方法时,才会加载其静态内部类,并同时初始化instance,所以这个也算是懒汉式。
classLoader
加载内部静态类时,会直接初始化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
| /**
* 饿汉式单例
* 特点:用前加载,天生线程安全,不如懒汉式灵活
*/
public class Singleton implements Serializable {
/**
* 核心为静态变量
* 加volatile确保异步可见性
*/
private static final Singleton instance = new Singleton();
/**
* 私有构造方法
*/
private Singleton() {
}
/**
* 工厂方法
*
* @return
*/
private static Singleton getInstance() {
return instance;
}
}
|
枚举饿汉式(线程安全)
1
2
3
4
| public enum SingletonEnum {
INSTANCE;
}
|
在java中,枚举就是一种特别的类,它具备class的一切特性,并且由于跟饿汉差不多,所以天然的线程安全。