这篇文章的由来
面试官:了解常用的设计模式吗?
我:了解。
面试官:说说DCL-单例模式。。。
我:不知道
面试官:。。。
⚠️设计模式是一种思想,是脱离语言的,Java有Java实现的方式,python有python的实现方式
解决的问题
多个线程操作不同实例对象,多个线程要操作同一对象,要保证对象的唯一性,如何保证实例化过程中只实例化一次?
解决的思路
1、有一个实例化的过程(只有一次),产生实例化对象
2、返回实例对象
恶汉式
实现:
在这里插入代码片/**
* @author csw
* @version 1.0
* @date 2022/2/22 12:02 上午
*/
public class HungerySingleton {
//加载的时候就产生的实例对象
private static HungerySingleton instance = new HungerySingleton();
private HungerySingleton(){
}
//返回实例对象
private static HungerySingleton getInstance() {
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(HungerySingleton.getInstance());
}).start();
}
}
}
优点:
-
- 线程安全性:在加载的时候已经被实例化,所以只有这一次,线程安全的
缺点:
-
- 懒加载:没有延迟加载,好长时间不使用还占用空间,影响性能
⚠️:代码中加static的原因是让对象属性属于类,达到共享的目的,如果不使用private的话默认是public外部可访问,也是不安全
懒汉式
/**
* @author csw
* @version 1.0
* @date 2022/2/22 12:29 上午
*/
public class HoonSingleton {
private static HoonSingleton instance = null;
private HoonSingleton() {
}
public static HoonSingleton getInstance() {
if(null == instance) {
instance = new HoonSingleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(HoonSingleton.getInstance());
}).start();
}
}
}
缺点:
-
- 线程安全:当A线程和B线程都等于null的时候能够同时访问,不能保证实例对象的唯一性
优点:
-
- 懒加载:使用的时候才去加载,性能比较好
懒汉式+同步方法
实现
/**
* @author csw
* @version 1.0
* @date 2022/2/22 12:29 上午
*/
public class HoonSynSingleton {
private static HoonSynSingleton instance = null;
private HoonSynSingleton() {
}
public static synchronized HoonSynSingleton getInstance() {
if(null == instance) {
instance = new HoonSynSingleton();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(HoonSynSingleton.getInstance());
}).start();
}
}
}
优点:
线程安全,满足了懒加载条件
缺点:
加入了synchronized,退化到了串行(队列)执行,性能低
DCL(Double-Check-Locking)
/**
* @author csw
* @version 1.0
* @date 2022/2/22 12:29 上午
*/
public class DCL {
private static DCL instance = null;
private DCL() {
}
public static DCL getInstance() {
if(null == instance) {
synchronized (DCL.class){
if(null == instance) {
instance = new DCL();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(DCL.getInstance());
}).start();
}
}
}
优点:
-
- 线程安全
-
- 懒加载
-
- 性能提高了
缺点
可能会因为指令重排导致出现空指针异常
Volatile+Double-check
加上了Volatile就能够避免因为指令重排出现空指针异常
Holder
声明类的时候,成员变量中不声明实例变量,而放到内部静态类中
实现:
/**
* @author csw
* @version 1.0
* @date 2022/2/22 1:30 上午
*/
public class HolderDemo {
private HolderDemo(){}
//内部类只有在实例化的时候才会去被调用
private static class Holder{
//加了static只能被实例化一次,因此满足了懒加载
private volatile static HolderDemo instance = new HolderDemo();
}
public static HolderDemo getInstance(){
return Holder.instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(HolderDemo.getInstance());
}).start();
}
}
}
优点:
既满足了懒加载、又可以不使用synchronized锁的方式保证了线程安全
是一种使用广泛的单例模式
枚举
实现
/**
* @author csw
* @version 1.0
* @date 2022/2/22 1:58 上午
*/
public class EnumSingletonDemo {
private EnumSingletonDemo() {
}
//懒加载
private enum EnumHolder{
//常量
INSTANCE;
private EnumSingletonDemo instance;
EnumHolder() {
this.instance = new EnumSingletonDemo();
}
private EnumSingletonDemo getInstance() {
return instance;
}
}
//恶汉式的实现
public static EnumSingletonDemo getInstance(){
return EnumHolder.INSTANCE.instance;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
System.out.println(EnumSingletonDemo.getInstance());
}).start();
}
}
}
优点:
这种方式写的很优雅,满足了对单例模式的所有条件,也是一种特别广泛使用的方式
缺点:
比较抽象化,需要一定的基础
彩蛋
下回面试又问到这个知识点
我:DCL就是巴拉巴拉。。。,还有更好的实现方式,vdc和枚举就是巴拉巴拉。。。
面试官:牛批。。。