美文网首页
单例模式深入解析

单例模式深入解析

作者: 丸子哒哒哒 | 来源:发表于2019-04-26 13:36 被阅读0次

版权声明

版权声明:本文为博主原创文章,转载请注明出处+地址

前言

相信各位对单例模式都不陌生,这已经是一个老生常谈的设计模式。之前我对于单例模式的理解仅仅停留在表面上,知道有几种,知道如何实现,知道大概的区别如何,但是其实单例模式还有很多不为人知的另一面,接下来我们就一起来看看。

单例模式的五种写法

方案一: 饿汉式

public class Singleton {   
    private static Singleton = new Singleton();
    private Singleton() {}
    public static getSignleton(){
        return singleton;
    }
}

方案二:懒汉式

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){}
    public static Singleton getSingleton() {
        if(singleton == null) singleton = new Singleton();
        return singleton;
    }
}

方案三:线程安全

public class Singleton {
    private static volatile Singleton singleton = null;
 
    private Singleton(){}
 
    public static Singleton getSingleton(){
        synchronized (Singleton.class){
            if(singleton == null){
                singleton = new Singleton();
            }
        }
        return singleton;
    }    
}

方案四:双重校验锁

public class Singleton {
    private static volatile Singleton singleton = null;
 
    private Singleton(){}
 
    public static Singleton getSingleton(){
        if(singleton == null){
            synchronized (Singleton.class){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }    
}

方案五:静态内部类

public class Singleton {
    private static class Holder {
        private static Singleton singleton = new Singleton();
    }
 
    private Singleton(){}
 
    public static Singleton getSingleton(){
        return Holder.singleton;
    }
}

方案六:枚举

public enum Singleton {
    INSTANCE;
}

单例模式的线程安全

方案一和方案二,非线程安全;
方案三和方案四,将唯一的实例进行判空操作,以及实例化的部分进行了synchronized进行加锁,实现了部分线程安全,那么还有什么隐患呢?那就是指令重排优化。
这里我们着重讲一下volatile修饰符,

volatile

volatile,有两层含义。第一层,可见性。第二层,防止重排序。
在类加载的过程中,分为以下几个步骤:

  • 分配内存空间
  • 初始化
  • 将 singleton 对象指向分配的内存地址

在没有volatile修饰之前,初始化和指向分配内存地址的执行顺序不可预测,那么就会导致一个问题:

在线程A正在进行类加载的过程中,恰好先执行了指向分配内存地址这一步,此时singleton已经不为null,但是由于还没有初始化,所以singleton也不能算是一个正常的实例对象。这个时候切换到线程B,由于singleton此时已经不是null,所以会直接返回,拿去调用一些方法,这个时候就会抛出异常。

volatile是如何保证线程安全的呢?

那么volatile的出现保证了在类加载的过程中,先进行初始化,再将singleton对象指向分配的内存地址。
在线程A进行类加载的时候,假如执行到初始化这一步,线程B的请求过来,由于此时singleton是null的,所以会被synchronized同步锁锁住,直到singleton实例化成功,再进行调用。

方案五,将实例放置在静态内部类中,静态内部类只会被加载一次,实现了线程安全。
方案六,枚举的内部实现自动帮我们实现了线程安全。

单例模式的反射安全

针对除了枚举单例以外的方案来说,为什么会有反射不安全呢?如果用户通过类的反射去调用构造函数,获取实例,那么我们的单例就不是真正意义上的单例模式了,那么如何解决这个问题呢?

在构造函数中,对singleton判空,如果非空,那我们就抛出异常,防止其反射调用。
【枚举自带防止反射强行调用的构造器】

单例模式的序列化安全

针对singleton实例的序列化和反序列化得到的对象是新的对象,那么这样就破坏了singleton的唯一性。
那么为什么在序列化的过程中会生成新的对象呢?
因为序列化会通过反射调用无参构造函数创建一个新的对象,如何避免呢?我们就回到了上一个问题,怎么去规避反射调用singleton的构造函数。
【枚举单例实现了自动序列化机制,防止了反序列化的时候创建新的对象】

总结

单例的四大要点:

  • 线程安全
  • 反射安全
  • 序列化与反序列化安全
  • 延迟加载

推荐阅读

为什么枚举单例的内部实现能保证线程安全以及序列化安全?

相关文章

  • 单例模式深入解析

    版权声明 版权声明:本文为博主原创文章,转载请注明出处+地址 前言 相信各位对单例模式都不陌生,这已经是一个老生常...

  • 【Java】设计模式 —— 深入浅出单例模式

    学习笔记 参考:深入浅出单实例SINGLETON设计模式单例模式【Java】设计模式:深入理解单例模式 场景:一般...

  • 枚举单例——避免反序列化破坏单例

    六种单例模式实现 枚举单例 深度解析单例与序列化

  • 设计模式之单例模式

    单例设计模式全解析 在学习设计模式时,单例设计模式应该是学习的第一个设计模式,单例设计模式也是“公认”最简单的设计...

  • 单例模式(Java内部类加载顺序)

    你真的会写单例模式吗——Java实现Android设计模式源码解析之单例模式深度分析 Java 的枚举类型:枚举的...

  • 设计模式——单例模式

    1.单例模式介绍 单例模式是应用最广的模式,也是我最先知道的一种设计模式,在深入了解单例模式之前,每当遇到如get...

  • 设计模式整理(2) 单例模式

    学习《Android 源码设计模式解析与实践》系列笔记 什么是单例 单例模式是应用最广,也是最容易理解的模式之一。...

  • 谈一谈iOS单例模式

    这篇文章主要和大家谈一谈iOS中的单例模式,单例模式是一种常用的软件设计模式,想要深入了解iOS单例模式的朋友可以...

  • 单例模式详解

    单例模式是最常用的设计模式之一,不管是工作中,还是面试中,单例模式一直都是宠儿。单例模式看似简单,但是如果深入理解...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

网友评论

      本文标题:单例模式深入解析

      本文链接:https://www.haomeiwen.com/subject/itdfnqtx.html