单例模式
--什么是单例模式?
--使用单例的应用场景?
--有哪几种单例模式?优缺点?
--单例模式的选择
定义
指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
应用场景
- 在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
- 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
- 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
五种单例模式
1.饿汉式
类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
public class HungrySingleton {
private static final HungrySingleton instance =new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return instance;
}
}
以空间换时间,线程安全。
2.懒汉式
类加载时没有生成单例,只有当第一次调用getInstance方法时才去创建这个单例。
public class LazySingleton {
private static volatile LazySingleton instance = null;
private LazySingleton() {} //private 避免类在外部被实例化
//getInstance方法前加同步
public static synchronized LazySingleton getInstance(){
if(instance == null ){
instance = new LazySingleton();
}
return instance;
}
}
3.双重检查锁
public class LazySingleton {
private static volatile LazySingleton instance = null;
private LazySingleton() {} //private 避免类在外部被实例化
public static LazySingleton getInstance2(){
if(instance == null){
synchronized (LazySingleton.class){
//为啥不能锁instance
if (instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
volatile关键字在这里的用处是禁止重排序,在new一个对象的时候JVM分了三个步骤:
- 分配内存空间。
- 初始化对象。
- 将内存空间的地址赋值给对应的引用。
步骤2和3的顺序是乱序执行的,也就是有可能是23或32。在多线程环境下,假如线程A执行了32的顺序,就有可能暴露了一个还没有初始化的对象给其他线程。
4.静态内部类模式
public class LazySingleton {
private static class LazySingletonHolder {
public static LazySingleton lazySingleton = new LazySingleton();
}
public static LazySingleton getInstance3(){
return LazySingletonHolder.lazySingleton;
}
}
优点:可以避免双重校验锁的同步的开销,而且也是线程安全
缺陷:静态内部类无法传参
5.枚举式
public enum EnumSingleton {
INSTANCE;
public EnumSingleton getInstance() {
return INSTANCE;
}
}
优点:可以阻止防序列化和反射攻击,在考虑到以上两种情况时,选择枚举式
调试
public static void main(String[] args) {
for(int i = 0; i < 200; i++){
new Thread(() ->
System.out.println(Thread.currentThread().getName() + ":" +
EnumSingleton.INSTANCE.hashCode())).start();
}
}
参考:
happens-before原则 https://www.jianshu.com/p/b9186dbebe8e , https://blog.csdn.net/u011116672/article/details/50147911
DCL问题解决 https://www.jianshu.com/p/ca19c22e02f4
延迟初始化 https://www.infoq.cn/article/double-checked-locking-with-delay-initialization/
为啥能阻止反序列化和防止反射攻击 https://www.cnblogs.com/chiclee/p/9097772.html
不负光阴不负卿