java设计模式之单例模式

单例模式

模式定义:
保证一个类只有一个实例,并且提供一个全局访问点
场景:
重量级的对象,不需要多个实例,如线程池,数据库连接池。
 
类图:
 

懒汉模式 

延迟加载,只有在真正使用的时候,才开始实例化。
    1)线程安全问题   
    2)double check 加锁优化
    3)编译器(JIT),CPU有可能对指令进行重排序,导致使用到尚未初始化的实例,可以通过添加volatile关键字进行修饰
          对于volatile修饰的字段,可以防止指令重排。
public class LazySingleton{
    private volatile static LazySingleton instance;
    private LazySingleton(){

    }

    public static LazySingleton getInstance(){
        if(instance == null){
            synchronized(LazySingleton.class){
                if(instance == null){
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

饿汉模式      

类加载的初始化阶段就完成了实例的初始化。本质上就是借助于JVM类加载机制,保证实例的唯一性(初始化过程只会执行一次)及线程安全(JVM以同步的形式来完成类加载的整个过程)

类加载过程

1)加载二进制数据到内存中,生成对应的Class数据结构

2)连接:验证->准备(给类的静态成员变量赋默认值)->解析

3)初始化:给类的静态变量赋初值 只有在真正使用对应的类时,才会触发初始化,如当前类是启动类即main函数所在类、直接进行new操作、访问静态属性、访问静态方法、用反射访问类、初始化一个类的子类等

public class HungrySingleton{
    private static HungrySingleton instance = new HungrySingleton();
    private HungrySingleton(){

    }
    public static HungrySingleton getInstance(){
        return instance;
    }
}

静态内部类      

1)本质上是利用类的加载机制来保证线程安全      

2)只有在实际使用的时候,才会触发类的初始化,所以也是懒加载的一种形式

public class InnerClassSingleton{

    private static class InnerClassHolder{
        private static InnerClassSingleton instance = new InnerClassSingleton();
    }
    private InnerClassSingleton(){
        
    }
    public static InnerClassSingleton getInstance(){
        return InnerClassHolder.instance;
    }

}

反射攻击实例

Constructor<InnerClassSingleton> declaredConstructor=InnerClassSingleton.c lass.getDeclaredConstructor(); 
declaredConstructor.setAccessible( true ); 
InnerClassSingleton innerClassSingleton=declaredConstructor.newInstance(); 

InnerClassSingleton instance=InnerClassSingleton.getInstance(); 
System.out.println(innerClassSingleton==instance);

静态内部类防止反射破坏

public 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)天然不支持反射创建对应的实例,且有自己的反序列化机制

2)利用类加载机制保证线程安全
public enum EnumSingleton {
    INSTANCE;

    public void print(){
        System.out.println(this.hashCode());
    }
}

序列化

可以利用指定方法来替换从反序列化流中的数据,代码如下

Serializable类中对于方法readResolve()的说明:

public class InnerClassSingleton implements Serializable {

    public static final long serialVersionUID = 42L;

    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;
    }

    public Object readResolve() throws ObjectStreamException{
        return InnerClassHolder.instance;
    }
}

源码中的应用

Spring & JDK 
 java.lang.Runtime 
 org.springframework.aop.framework.ProxyFactoryBean 
 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 
 org.springframework.core.ReactiveAdapterRegistry 

Tomcat
 org.apache.catalina.webresources.TomcatURLStreamHandlerFactory

反序列化指定数据源 
 java.util.Currency

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值