java设计模式之单例模式singleton

  • 模式定义

保证一个类只有一个实例,并提供一个全局访问点。

 

  • 单例模式的实现主要有以下四种

一、懒汉模式

public class LazySingleton {
    private static volatile LazySingleton instance;
    //不允许被实例化
    private LazySingleton(){
    }
    /**
     * 获取实例
     * @return
     */
    public static LazySingleton getInstance() {
        if(instance!=null){
            synchronized (LazySingleton.class){
                if(instance!=null) {
                    instance = new LazySingleton();
                    /*一个new操作在字节码层面对应三步操作
                    * 1、开辟空间
                    * 2、初始化
                    * 3、变量赋值
                    * cpu可能会重排需2、3步。如果先执行了第3步,则会返回一个没有初始化的对象,
                    * 发生空指针异常。增加volatile关键字,防止重排序
                    * */
                }
            }
        }
        return instance;
    }
}

特点:延时加载,只有在正在需要的时候才开始实例化。

1、线程安全问题

2、双重判断double check,加锁优化(锁也可以直接加锁到方法上,但是会耗费性能)

3、使用volatile关键字,防止指令重排序

 

二、饿汉模式

class HunglySingleton {
    private static HunglySingleton instance= new HunglySingleton();
    //不允许被实例化
    private HunglySingleton(){
    }
    /**
     * 获取实例
     * @return
     */
    public static HunglySingleton getInstance() {
        return instance;
    }
}

特点:类加载的初始化阶段就完成了实例的初始化,本质上是借助于jvm的类加载机制,保证实例的唯一性。(急加载)

类加载机制:

1、加载:加载二进制文件到内存中,并生成对应的class数据结构

2、连接:包括验证、准备(给静态变量赋默认值,引用类型为null,int为0,boolean为false等等)、解析(将符号应用替换为直接引用)

3、初始化(执行静态代码块、给静态变量赋初值)

注意:如果当前类的父类没有被加载,则会先加载父类

三、静态内部类模式

class InnerSingleton {
    private static class InnerSingletonHolder {
        private static InnerSingleton instance= new InnerSingleton();
    }
    //不允许被实例化
    private InnerSingleton(){
    }
    /**
     * 获取实例
     * @return
     */
    public static InnerSingleton getInstance() {
        return InnerSingletonHolder.instance;
    }
}

特点:也是借助jvm的类加载机制,保证实例的唯一性(懒加载)

当访问instance时才会初始化实例

四、enum模式

enum  EnumSingleton {
    INSTANCE;
    /**
     * 获取实例
     * @return
     */
    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

特点:简单,线程安全,也能够防护反射攻击(禁止反射实例化enum类型类)。

 

特殊情况:

一、当使用反射创建实例时还能保证单例吗?

1、懒汉模式:无解

2、饿汉模式

解决方法:把构造方法增加判断,如果使用反射调用私有构造方法,则会抛异常。

    //不允许被实例化
    private HunglySingleton(){
        if(instance!=null){
            throw new RuntimeException("单例不允许多例实例");
        }
    }

3、静态内部类模式

同饿汉模式的解决办法

4、enmu模式

天然的,就不能被反射调用创建实例,当使用反射newInstance()就会抛异常。

二、当使用序列化反序列化创建实例时还能保证单例吗?

当我们对静态内部类模式测试一下,序列化并反序列化后,还是同一个实例吗?

public class InnerSingletonTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        InnerSingleton instance = InnerSingleton.getInstance();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("innerSingletonObject"));
        objectOutputStream.writeObject(instance);
        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("innerSingletonObject"));
        InnerSingleton instance2 = (InnerSingleton)objectInputStream.readObject();
        objectInputStream.close();

        System.out.println(instance==instance2);
    }
}

class InnerSingleton implements Serializable {
    private static class InnerSingletonHolder {
        private static InnerSingleton instance= new InnerSingleton();
    }
    //不允许被实例化
    private InnerSingleton(){
        if(InnerSingletonHolder.instance!=null){
            throw new RuntimeException("单例不允许多例实例");
        }
    }
    /**
     * 获取实例
     * @return
     */
    public static InnerSingleton getInstance() {
        return InnerSingletonHolder.instance;
    }
}

 答案是false。因为反序列化没有调用InnerSingleton的构造方法。

解决办法: 

查看serializable接口的说明如下

 需要增加一个readResolve方法

    Object readResolve() throws ObjectStreamException{
        return getInstance();
    }

注意:enum枚举类除外,有单独的处理方法,查看readObject()的源码如下:

 

  • jdk和spring中单例模式的应用

 jdk:Runtime类(饿汉)、Currency

spring:DefaultSingletonBeanRegistry、ReactiveAdapterRegistry、ProxyFactoryBean(aop包)

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值