单例模式多种玩法

饿汉单例模式

简单饿汉单例模式

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");

    }
}

在这里插入图片描述
可以看到线程间是单例的。

单例模式的本质

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

笔记和代码地址

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

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


微信订阅号:搜索: 怒放de每一天
在这里插入图片描述


个人微信公众号:
搜索: 怒放de每一天
不定时推送相关文章,期待和大家一起成长!!
在这里插入图片描述


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章