美文网首页
单例模式详解

单例模式详解

作者: 奋斗的韭菜汪 | 来源:发表于2020-06-15 23:33 被阅读0次

应用场景:是指确保一个类在任何情况下都绝对只有一个实例(实现:隐藏其所有的构造方法),并提供一个全局访问点(实现:getInstance方法)
源码应用点:ApplicationContext\servletContext\DBPool
单例可以分为四类:
1、饿汉式单例
2、懒汉式单例
3、注册式单例(其中最优雅的写法:枚举式单例)
4、ThreadLocal单例

private Object readResolve(return INSTANCE);
加上这个方法可以防止序列号和反序列化破坏单例(原因:反序列化获取的对象默认是readResolve通过浅拷贝,拷贝的是对象的引用,所以还是同一个实例对象)

ThreadLocal单例:在同一个线程内会的实例是相同的,ThreadLocal中有个重要对象ThreadLocalMap,它的key是this就是当前线程,纬度是一线程为单位
单例模式的缺点:没有接口扩展困难(违背开闭原则的)

重点总结:
1、私有化构造器
2、保证线程安全
3、延迟加载
4、防止序列化和反序列化破坏单例
5、防止反射破坏单例
1、饿汉式写法一

/**
 * 1、饿汉式写法一:类加载时就初始化
 * 优点:执行效率高,性能高(没有加任何锁)
 * 缺点:内存浪费(在某些情况下:在单例大量使用的情况);在类的加载时才初始化 
 * 会保证内存利用率高
 */
public class HungrySigleton {
    /**
     * 类加载时就初始化
     */
    private static final HungrySigleton hungrySigleton = new HungrySigleton();
    //构造函数私有化
    private HungrySigleton(){
    }
    //提供一个全局访问点
    public static HungrySigleton getHungrySigleton() {
        return hungrySigleton;
    }
}

2、饿汉式单例写法二

/**
 * 2、饿汉式单例写法二:
 */
public class HungryStaticSingleton {
    /**
     * 类加载顺序:
     * //先静态后动态
     * //先上,后下
     * //先属性,后方法
     */
    private static final HungryStaticSingleton hungryStaticSingleton;
    /**
     * 使用静态代码块复制,和写法一基本类似
     */
    static {
        hungryStaticSingleton = new HungryStaticSingleton();
    }
    public static HungryStaticSingleton getHungryStaticSingleton() {
        return hungryStaticSingleton;
    }
}

3、懒汉模式:外部需要时才实例化

/**
 * 3、懒汉模式:外部需要时才实例化
 * 优点:节省内存(需要时才实例化)
 * 缺点:线程不安全(多线程调用getInstance方法,有可能创建多个实例,单例失败)
 */
public class LazySimpleSingleton {
    //声明一个LazySimpleSingleton对象自己的全局变量
    private LazySimpleSingleton instance;
    //构造函数私有化
    private LazySimpleSingleton(){
    }
    //提供一个全局访问点
    public LazySimpleSingleton getInstance(){
        if (instance == null){
            instance = new LazySimpleSingleton();
        }
        return instance;
    }
    //例如两个线程同时调用getInstance,运行结果:
    //出现一个实例:1、正常顺序执行,2、后者覆盖前者情况
    //出现两个实例:同时进入条件,按顺序发返回
    //多线程调试:先断点,右击断点选择thread,执行方法,选择控制台Debugger下Frames,查看线程各个线程,选择执行哪个线程(
    // 线程执行顺序可以随意控制),
}
/**
 * 4、
 * 优点:节省了内存,线程安全
 * 缺点:加锁造成了性能瓶颈
 */
public class LazySimpleLockSingleton {
    //声明一个LazySimpleSingleton对象自己的全局变量
    private LazySimpleLockSingleton instance;
    //构造函数私有化
    private LazySimpleLockSingleton(){
    }
    //提供一个全局访问点
    public synchronized LazySimpleLockSingleton getInstance(){
        //java8之后可以用juc的重入锁
        //Lock lock = new ReentrantLock();
        //lock.lock();
        if (instance == null){
            instance = new LazySimpleLockSingleton();
        }
        //lock.unlock();
        return instance;
    }
    //例如两个线程同时调用getInstance,运行结果:
    //出现一个实例:1、正常顺序执行,2、后者覆盖前者情况
    //出现两个实例:同时进入条件,按顺序发返回
    //多线程调试:先断点,右击断点选择thread,执行方法,选择控制台Debugger下Frames,查看线程各个线程,选择执行哪个线程(
    // 线程执行顺序可以随意控制),
}

5、双重检查

/**
 * 5、双重检查
 * 优点:节省了内存,线程安全,性能高了
 * 缺点:可读性不好,不够优雅
 */
public class LazyDoubleSimpleSingleton {
    //声明一个LazySimpleSingleton对象自己的全局变量
    private volatile LazyDoubleSimpleSingleton instance;
    //构造函数私有化
    private LazyDoubleSimpleSingleton(){
    }
    //提供一个全局访问点
    public LazyDoubleSimpleSingleton getInstance(){
        //第一次检查是否需要阻塞
        if (instance == null) {
            synchronized (this) {
                //第二次检查是否需要阻塞
                if (instance == null) {
                    instance = new LazyDoubleSimpleSingleton();
                    //多线程情况下,有指令重排序问题(需要在声明全局变量的时候加上volatile)
                }
            }
        }
        return instance;
    }
}

6、

/**
 * 6
 * classPath:LazyStaticInnerClassSingleton.class(1)
 *           LazyStaticInnerClassSingleton$LazyHolder.class(2)
 *           加载类1时不会加载类2,只用当外部使用到getInstance时才会触发加载类2
 * 优点:写法优雅,利用了Java本身的语法特点(性能高,避免了内存浪费,不会有指令重拍问题)
 * 缺点:能够被反射破坏(前面5个也能被反射破坏,为什么:前面六种都是私有化构造函数,但是反射能够从外部拿到类的私有化构造
 * 通过设置构造方法setAccessible为true,强制访问私有构造方法)
 */
public class LazyStaticInnerClassSingleton {
    private LazyStaticInnerClassSingleton(){
        //改进方法
        if (LazyHolder.INSTANCE != null) {
            throw new RuntimeException("不允许非法访问");
        }
    }
    private static LazyStaticInnerClassSingleton getInstance(){
        return LazyHolder.INSTANCE;
    }

    //静态内部类(静态变量在加载的时候就会被分配内存空间,静态内部类不一样,只在使用类时才分配内存)
    private static class LazyHolder{
        private static final LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
    }
}
/**7、
 * 枚举式单例(最优雅的写法)
 * 不能用反射创建枚举对象
 * 内存浪费(在某些情况下:在单例大量使用的情况)
 */
public enum EnumSingleton {
    //单例对象
    INSTANCE;
    //自定义属性给单例赋值
    private Object data;
    public Object getData() {
        return data;
    }
    public void setData(Object data) {
        this.data = data;
    }
    //全局调用点
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}

相关文章

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • iOS 单例模式

    关于单例模式的详解,看完这几篇,就完全了然了。iOS 单例模式iOS中的单例模式iOS单例的写法

  • 单例模式

    单例模式及C++实现代码单例模式4种实现详解 c++11改进我们的模式之改进单例模式 单例模式(Singleton...

  • 单例模式安全之反射攻击

    单例模式安全之反射攻击 源码 单例模式这里就不谈了,什么是单例模式可参考七种Java单例模式详解,这里是关于单例模...

  • Java架构师课程

    Java架构班开学典礼 Spring中常用的设计模式概述及工厂模式详解 单例模式及原型模式单例模式及原型模式单例模...

  • 高并发编程-04-线程安全-单例模式详解

    单例模式详解 1,编写单例模式 饿汉式:不会存在线程安全的问题 public class Singleton1 {...

  • 单例模式

    转自单例模式详解 稍作修改 世间万物都有它的起源,那单利模式的诞生原因或者说情景是怎么样的呢?单例模式...

  • 单例模式详解

    一.前言 在日常开发中或者在面试中用到的设计模式最多的就是单例模式,这篇文章简单的讲一下单例,包括单例的概念,好处...

  • 单例模式详解

    什么是单例 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系...

网友评论

      本文标题:单例模式详解

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