单例模式多种玩法

简介: 单例模式多种玩法

饿汉单例模式


简单饿汉单例模式


public class HungrySingleton {
    private static final HungrySingleton hungrySington = new HungrySingleton();
    private HungrySingleton() {
    }
    public static HungrySingleton getInstance(){
        return hungrySington;
    }
}


静态代码块实现饿汉模式


public class HungryStaticSingleton {
    private static final HungryStaticSingleton hungrySington;
    static {
        hungrySington = new HungryStaticSingleton();
    }
    private HungryStaticSingleton() {
    }
    public static HungryStaticSingleton getInstance() {
        return hungrySington;
    }
}


懒汉单例模式


简单懒汉单例模式(线程不安全)


public class LazySimplySington {
    private static LazySimplySington lazy;
private LazySimplySington() {
    }
    //jdk1.8之后对synchronized性能优化不少
    //不可避免还是存在一定的性能问题
    public synchronized static LazySimplySington getInstance() {
        if (lazy == null) {
            lazy = new LazySimplySington();
        }
        return lazy;
    }
}


双重检查实现单例模式(线程安全)


注意指令重排序问题,需要单独加voliate关键字


public class LazyDoubleCheckSington {
    private volatile static  LazyDoubleCheckSington lazy = null;
    private LazyDoubleCheckSington() {
    }
    //jdk1.8之后对synchronized性能优化不少
    //不可避免还是存在一定的性能问题
    public static LazyDoubleCheckSington getInstance() {
        if (lazy == null) {
            synchronized (LazyDoubleCheckSington.class) {
                if (lazy == null) {
                    lazy = new LazyDoubleCheckSington();
                    //指令重排序的问题:也就是 第二步和第三步会颠倒,
                    // 解决方式 变量上加voliate,让线程可见
                    //CPU执行时候会转换成JVM指令执行
                    //1.分配内存给这个对象
                    //2.初始化对象
                    //3.将初始化好的对象和内存地址建立关联,赋值
                    //4.用户初次始化
                }
            }
        }
        return lazy;
    }
}


内部类实现单例模式(线程安全)


public class LazyInnerClassSington {
    //虽然构造方法有了,但是逃不过反射的法眼
    private LazyInnerClassSington() {      
    }
    //懒汉式单例
    //LazyHolder里面的逻辑需要等到外部方法调用时才执行
    //巧妙利用了内部类的特性
    //JVM底层执行逻辑,完美的避免了线程安全问题
    public static final LazyInnerClassSington getInstance(){
        return LazyHolder.lazy;
    }
    private static class LazyHolder{
        private static final LazyInnerClassSington lazy = new LazyInnerClassSington();
    }
}


暴力破解单例


反射暴力破解


public class LazyInnerClassSingtonTest {
    public static void main(String[] args) {
        try {
            //反射,破坏了单例
            Class<?> clazz = LazyInnerClassSington.class;
            Constructor<?> c = clazz.getDeclaredConstructor(null);
            c.setAccessible(true);
            Object o = c.newInstance();
            Object o2 = c.newInstance();
            System.out.println(o == o2);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}


解决方式:


在构造函数内部添加判断


//虽然构造方法有了,但是逃不过反射的法眼
    private LazyInnerClassSington() {
        if (LazyHolder.lazy != null){
            throw new RuntimeException("不允许构建多个实例");
        }
    }


测试:


完整代码:


public class LazyInnerClassSington {
    //虽然构造方法有了,但是逃不过反射的法眼
    private LazyInnerClassSington() {
        if (LazyHolder.lazy != null){
            throw new RuntimeException("不允许构建多个实例");
        }
    }
    //懒汉式单例
    //LazyHolder里面的逻辑需要等到外部方法调用时才执行
    //巧妙利用了内部类的特性
    //JVM底层执行逻辑,完美的避免了线程安全问题
    public static final LazyInnerClassSington getInstance(){
        return LazyHolder.lazy;
    }
    private static class LazyHolder{
        private static final LazyInnerClassSington lazy = new LazyInnerClassSington();
    }
}


序列化和反序列化暴力破解


来个饿汉单例


//反序列化时导致单例破坏
public class SeriableSingleton implements Serializable {
    //序列化就是说把内存中的状态通过转换成字节码的形式
    //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
    //内存中状态给永久保存下来了
    //反序列化
    //讲已经持久化的字节码内容,转换为IO流
    //通过IO流的读取,进而将读取的内容转换为Java对象
    //在转换过程中会重新创建对象new
    public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}
    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }


破解


public class SeriableSingletonTest {
    public static void main(String[] args) {
        SeriableSingleton s1 = null;
        SeriableSingleton s2 = SeriableSingleton.getInstance();
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();
            FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SeriableSingleton)ois.readObject();
            ois.close();
            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


解决方式resolve方法


加个方法:


//序列化解决单例问题的方式
    //重写readResolve方法,只不过是覆盖了反序列化出来的对象
    //还是创建了两次,发生在JVM层面,相对来说比较安全
    //之前反序列化出来的对象会被GC回收
    private  Object readResolve(){
        return  INSTANCE;
    }


完整代码:


//反序列化时导致单例破坏
public class SeriableSingleton implements Serializable {
    //序列化就是说把内存中的状态通过转换成字节码的形式
    //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)
    //内存中状态给永久保存下来了
    //反序列化
    //将已经持久化的字节码内容,转换为IO流
    //通过IO流的读取,进而将读取的内容转换为Java对象
    //在转换过程中会重新创建对象new
    public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}
    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }
    //序列化解决单例问题的方式
    //重写readResolve方法,只不过是覆盖了反序列化出来的对象
    //还是创建了两次,发生在JVM层面,相对来说比较安全
    //之前反序列化出来的对象会被GC回收
    private  Object readResolve(){
        return  INSTANCE;
    }
}


注册类单例


枚举实现单例(推荐)


可以同时实现防止反射和序列化反序列化暴力破解


public enum EnumSingleton {
    INSTANCE;
    private String name;
    public String getData() {
        return name;
    }
    public void setData(String data) {
        this.name = data;
    }
    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}


使用jad工具查看枚举类的反编译代码



反编译代码:


// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingleton.java
package com.example.sington.register;
public final class EnumSingleton extends Enum
{
    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }
    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(com/example/sington/register/EnumSingleton, name);
    }
    private EnumSingleton(String s, int i)
    {
        super(s, i);
    }
    public Object getData()
    {
        return data;
    }
    public void setData(Object data)
    {
        this.data = data;
    }
    public static EnumSingleton getInstance()
    {
        return INSTANCE;
    }
    public static final EnumSingleton INSTANCE;
    private Object data;
    private static final EnumSingleton $VALUES[];
    static 
    {
        INSTANCE = new EnumSingleton("INSTANCE", 0);
        $VALUES = (new EnumSingleton[] {
            INSTANCE
        });
    }
}


容器实现单例


public class ContainerSingleton {
    private ContainerSingleton() {
    }
    private static Map<String, Object> ioc = new ConcurrentHashMap<>();
    public static Object getBean(String className) {
        synchronized (ioc){
            if (!ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return ioc.get(className);
        }
    }
}


public class Pojo {
}


public class ContainerSingletonTest {
    public static void main(String[] args) {
        try {
            long start = System.currentTimeMillis();
            ConcurrentExecutor.execute(new ConcurrentExecutor.RunHandler() {
                public void handler() {
                    Object obj = ContainerSingleton.getBean("com.example.Pojo");
                    System.out.println(System.currentTimeMillis() + ": " + obj);
                }
            }, 10,6);
            long end = System.currentTimeMillis();
            System.out.println("总耗时:" + (end - start) + " ms.");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}


线程间实现单例ThreadLocal


public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
            new ThreadLocal<ThreadLocalSingleton>() {
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };
    private ThreadLocalSingleton() {
    }
    public static ThreadLocalSingleton getInstance() {
        return threadLocalInstance.get();
    }
}


public class ExectorThread implements Runnable {
    @Override
    public void run() {
//        LazySimplySington instance = LazySimplySington.getInstance();
//        LazyDoubleCheckSington instance = LazyDoubleCheckSington.getInstance();
        ThreadLocalSingleton instance = ThreadLocalSingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+" ---"+instance);
        ThreadLocalSingleton instance2 = ThreadLocalSingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+" ---"+instance2);
        ThreadLocalSingleton instance3 = ThreadLocalSingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+" ---"+instance3);
        ThreadLocalSingleton instance4 = ThreadLocalSingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+" ---"+instance4);
        ThreadLocalSingleton instance5 = ThreadLocalSingleton.getInstance();
        System.out.println(Thread.currentThread().getName()+" ---"+instance5);
        System.out.println(Thread.currentThread().getName()+" ---"+instance);
    }
}


public class ThreadLocalSingletonTest {
    public static void main(String[] args) {
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());
        System.out.println(ThreadLocalSingleton.getInstance());
        Thread t1 = new Thread(new ExectorThread());
        Thread t2 = new Thread(new ExectorThread());
        t1.start();
        t2.start();
        System.out.println("End");
    }
}

1dc618a0ed9580ce8bfa6facb208c08f.png

可以看到线程间是单例的。


单例模式的本质


本质: 控制实例数目。(研磨设计模式)。


笔记和代码地址


代码:


https://github.com/hufanglei/pattern-learn/tree/master/src/main/java/com/example/sington


笔记https://blog.csdn.net/baidu_21349635/article/details/106067581


相关文章
|
7月前
|
设计模式 算法 前端开发
【面试题】什么是策略模式?用了策略模式之后,再也不用写那么多 if else 了,真香!
【面试题】什么是策略模式?用了策略模式之后,再也不用写那么多 if else 了,真香!
102 0
|
7月前
|
设计模式
二十三种设计模式:解密职责链模式-购物优惠活动的设计艺术
二十三种设计模式:解密职责链模式-购物优惠活动的设计艺术
|
7月前
|
设计模式 Java
【设计模式】腾讯二面:自动贩卖机/音频播放器使用了什么设计模式?
【设计模式】腾讯二面:自动贩卖机/音频播放器使用了什么设计模式?
48 1
|
设计模式 C#
C# 机房重构单例模式
C# 机房重构单例模式
73 0
|
设计模式
大话设计模式——单例模式(前奏)
大话设计模式——单例模式(前奏)
87 0
|
设计模式 SQL 安全
【设计模式学习笔记】单例模式详解(懒汉式遇上多线程问题解析基于C++实现)
【设计模式学习笔记】单例模式详解(懒汉式遇上多线程问题解析基于C++实现)
353 0
【设计模式学习笔记】单例模式详解(懒汉式遇上多线程问题解析基于C++实现)
阿里面试官:使用策略模式+工厂模式干掉代码中过多的if-else
过多if-else项目背景 如果一开始就知道现在的业务需要,大部分人都不会在代码里添加过多的if-else判断的,烂代码基本都是刚开始写代码时并没有太多的需求,随着期需求不断的修改增加,开发时间也较的紧张,代码往往都是怎么快速怎么写。当然多写一个if-else比使用各种设计模式肯定来的更快速了,这也就导致项目代码慢慢变得臃肿,难以维护的主要原因。在有空闲时间的情况下就可以给以前的代码做一次手术了。先看本次未优化前的代码:
|
设计模式 安全 Java
透彻理解单例模式
主要内容有: 该模式的介绍,包括: 引子、意图(大白话解释) 类图、时序图(理论规范) 该模式的代码示例:熟悉该模式的代码长什么样子 该模式的优缺点:模式不是万金油,不可以滥用模式 该模式的实际使用案例:了解它在哪些重要的源码中被使用
222 0
|
安全 Java 编译器
漫画:什么是单例模式?(整合版)
为什么这样写呢?我们来解释几个关键点: 1.要想让一个类只能构建一个对象,自然不能让它随便去做new操作,因此Signleton的构造方法是私有的。 2.instance是Singleton类的静态成员,也是我们的单例对象。它的初始值可以写成Null,也可以写成new Singleton()。至于其中的区别后来会做解释。 3.getInstance是获取单例对象的方法。
177 0
漫画:什么是单例模式?(整合版)
机房重构之单例模式的应用
机房重构之单例模式的应用