1、单例设计模式(Singleton)
1.1模式定义/应用场景/类图分析
定义:保证一个类只有一个实例,并且提供一个全局访问点
场景:重量级对象,不需要多个实例,如线程池,数据库链接池
1.1.1懒汉模式
-
延迟加载,只有真正使用的时候,才开始实例化。
-
线程安全问题
-
double check枷锁优化
-
编译器(JIT),CPU有可能对指令进行重排序,导致使用到未初始化的实例,可以通过添加volatile关键字进行修饰。
注:对于volatile 修饰的字段,可以防止指令重排
package com.singleton.v1; public class LazySingletonTest { public static void main(String[] args) { /* LazySingleton lazySingleton=LazySingleton.getInstance(); LazySingleton lazySingleton1=LazySingleton.getInstance(); System.out.println(lazySingleton==lazySingleton1);*/ new Thread(()->{ LazySingleton lazySingleton=LazySingleton.getInstance(); System.out.println(lazySingleton); }).start(); new Thread(()->{ LazySingleton lazySingleton=LazySingleton.getInstance(); System.out.println(lazySingleton); }).start(); } } class LazySingleton{ private volatile static LazySingleton instance; private LazySingleton() { } public static LazySingleton getInstance() { if(instance==null){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(LazySingleton.class){ if(instance==null){ instance=new LazySingleton(); } } } return instance; } }
1.1.2饿汉模式
类加载的初始化阶段就完成了实例的初始化。本质上就是借助与JVM类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)。
类加载过程:
-
加载二进制数据到内存中,生成对应的Class数据结构
-
链接:a.验证 b.准备(给类的静态成员变量赋默认值)c.解析
-
初始化:给类的静态变量赋初值
只有在真正使用对应的类时,才会触发初始化 如( 当前类是启动类即 main函数所在类,直接进行new 操作,访问静态属性、访问静态方 法,用反射访问类,初始化一个类的子类等.)
package com.singleton.v1;
public class HungrySingletonTest {
public static void main(String[] args) {
HungrySingleton hungrySingleton=HungrySingleton.getInstance();
HungrySingleton hungrySingleton1=HungrySingleton.getInstance();
System.out.println(hungrySingleton==hungrySingleton1);
}
}
class HungrySingleton{
private static HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){
}
public static HungrySingleton getInstance() {
return instance;
}
}
1.1.3静态内部类
-
本质上是利用类的加载机制来保证线程安全
-
只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种的方式
package com.singleton.v1;
public class InnerClassSingletonTest {
public static void main(String[] args) {
InnerClassSingleton innerClassSingleton=InnerClassSingleton.getInstance();
InnerClassSingleton innerClassSingleton1=InnerClassSingleton.getInstance();
System.out.println(innerClassSingleton==innerClassSingleton1);
}
}
class InnerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
private InnerClassSingleton(){
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.instance;
}
}
1.1.4.反射攻击实例
饿汉模式与静态内部类可以通过在构造函数中防止反射攻击,懒汉模式无法防止。
public class InnerClassSingletonTest {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
InnerClassSingleton innerClassSingleton=InnerClassSingleton.getInstance();
InnerClassSingleton innerClassSingleton1=InnerClassSingleton.getInstance();
System.out.println(innerClassSingleton==innerClassSingleton1);
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton2 = declaredConstructor.newInstance();
System.out.println(innerClassSingleton1==innerClassSingleton2);
}
}
class InnerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
private InnerClassSingleton(){
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.instance;
}
}
静态内部类处理反射攻击
public class InnerClassSingletonTest {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
InnerClassSingleton innerClassSingleton=InnerClassSingleton.getInstance();
InnerClassSingleton innerClassSingleton1=InnerClassSingleton.getInstance();
System.out.println(innerClassSingleton==innerClassSingleton1);
Constructor<InnerClassSingleton> declaredConstructor = InnerClassSingleton.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
InnerClassSingleton innerClassSingleton2 = declaredConstructor.newInstance();
System.out.println(innerClassSingleton1==innerClassSingleton2);
}
}
class InnerClassSingleton{
private static class InnerClassHolder{
private static InnerClassSingleton instance=new InnerClassSingleton();
}
private InnerClassSingleton(){
if(InnerClassHolder.instance!=null){
throw new RuntimeException("单例不允许创建多个");
}
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.instance;
}
}
1.1.5枚举类型
1)天然不支持反射创建对应的实例,且有自己的反序列化机制
2)利用类加载机制保证线程安全
package com.singleton.v1;
public enum EnumSingleton {
INSTANCE;
public void print(){
System.out.println(this.hashCode());
}
}
class EnumTest{
public static void main(String[] args) {
EnumSingleton instance = EnumSingleton.INSTANCE;
EnumSingleton instance1 = EnumSingleton.INSTANCE;
System.out.println(instance==instance1);
}
}
1.2 单例模式在Spring框架&JDK源码中的应用
//JDK
java.lang.Runtime
// Spring
org.springframework.aop.framework.ProxyFactoryBean
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
org.springframework.core.ReactiveAdapterRegistry
// Tomcat
org.apache.catalina.webresources.TomcatURLStreamHandlerFactory
// 反序列化指定数据源
java.util.Currenc