• 关于Java单例


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

    参考资料:

    http://blog.csdn.net/haoel/article/details/4028232

    public class SingletonTest implements Runnable {
        static SingletonClass1 instance = null;
    
        public static void main(String args[]) {
            // SingletonClass0 instance = new SingletonClass0();// The constructor
            // SingletonClass0() is not visible
    
            for (int i = 0; i < 15; i++) {
                new Thread(new SingletonTest()).start();
            }
            // System.out.println(instance.getClass());
    
        }
    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            SingletonClass6.getInstance();
            // System.out.println(SingletonClass7.instance.hashCode());
        }
    }
    
    class SingletonClass0 {
        private static SingletonClass0 instance = null;
    
        private SingletonClass0() {
            System.out.println("constructor run");
        }
    
        public static SingletonClass0 getInstance() {// wrong 单线才单线程下没问题,多线程下仍会有多个实例
            if (instance == null) {
                instance = new SingletonClass0();
                System.out.println(instance.hashCode());
            }
            return instance;
        }
    }
    
    class SingletonClass1 {
        private static SingletonClass1 instance = null;
    
        private SingletonClass1() {
            System.out.println("constructor run");
        }
    
        public static SingletonClass1 getInstance() {// wrong 多线程下仍会有多个实例,与上一种
                                                        // 区别在于过个线程的new过程被同步了
            if (instance == null) {
                synchronized (SingletonClass1.class) {
                    instance = new SingletonClass1();
                    System.out.println(instance.hashCode());
                }
            }
            return instance;
        }
    }
    
    class SingletonClass2 {
        private static SingletonClass2 instance = null;
    
        private SingletonClass2() {
            System.out.println("constructor run");
        }
    
        public static SingletonClass2 getInstance() {
            synchronized (SingletonClass2.class) {// right。只会有一个线程new实例,但阻碍了后续线程读实例
                if (instance == null) {
                    instance = new SingletonClass2();
                    System.out.println(instance.hashCode());
                }
            }
            return instance;
        }
    }
    
    class SingletonClass3 {
        private static SingletonClass3 instance = null;
    
        private SingletonClass3() {
            System.out.println("constructor run");
        }
    
        public static SingletonClass3 getInstance() {
            synchronized (SingletonClass3.class) {// right
                                                    // 只会有一个线程new实例,不会影响后续线程读实例,但instance=new
                                                    // SingletonClass3()在JVM内不是原子操作,内部的几个步骤可能乱序,从而出错
                if (instance == null) {
                    synchronized (SingletonClass1.class) {
                        if (instance == null) {
                            instance = new SingletonClass3();
                            System.out.println(instance.hashCode());
                        }
                    }
                }
            }
            return instance;
        }
    }
    
    class SingletonClass4 {
        private static volatile SingletonClass4 instance = null;// right
                                                                // 只有一个实例,volatite保证了在JVM内部new
                                                                // instance的几个操作禁止指令重排序优化
    
        private SingletonClass4() {
            System.out.println("constructor run");
        }
    
        public static SingletonClass4 getInstance() {
            synchronized (SingletonClass4.class) {
                if (instance == null) {
                    synchronized (SingletonClass1.class) {
                        if (instance == null) {
                            instance = new SingletonClass4();
                            System.out.println(instance.hashCode());
                        }
                    }
                }
            }
            return instance;
        }
    }
    
    class SingletonClass5 {// right,但是由类加载器在类加载时创建实例,我们无法控制实例创建的时机以干一些事(比如某个配置文件,或是某个被其它类创建的资源)
        public volatile static SingletonClass5 instance = new SingletonClass5();
    
        private SingletonClass5() {
            System.out.println("constructor run");
        };
    
        public static SingletonClass5 getInstance() {
            System.out.println(instance.hashCode());
            return instance;
        }
    }
    
    class SingletonClass6 {// 仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了
                            // getInstance()
                            // 之外没有办法访问它,因此它只有在getInstance()被调用时才会真正创建;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖
                            // JDK 版本
        private static class SingletonHolder {
            private static final SingletonClass6 INSTANCE = new SingletonClass6();
        }
    
        private SingletonClass6() {
            System.out.println("constructor run");
        }
    
        public static final SingletonClass6 getInstance() {
            System.out.println(SingletonHolder.INSTANCE.hashCode());
            return SingletonHolder.INSTANCE;
        }
    }
    
    class SingletonClass7 {
        public SingletonClass7() {
            System.out.println("constructor run");
        }
    }
    
    enum SingletonClass {
        instance;
    }
    View Code

    http://mp.weixin.qq.com/s/q7-GWt87S7uJ9NY-s1z4SA

    需要参数:懒汉式1。(线程安全改进3、需要才new改进4)

    不需要参数:饿汉式2。(本身线程安全、需要才new改进5)

    上述几种实现方式都存在反射、序列化、反序列化使得单例被破坏的问题,可能成为漏洞被利用,虽可采用手段加以解决(解决方式见下)但不简单高效(若单例类维持了其他对象的状态时还需要使他们成为transient的对象,此时就更复杂了)  ->  使用枚举6。

    反序列化时单例被破坏的解决方法:readResolve

    public class Singleton implements java.io.Serializable {     
       public static Singleton INSTANCE = new Singleton();     
    
       protected Singleton() {     
       }  
    
       //反序列时直接返回当前INSTANCE
       private Object readResolve() {     
                return INSTANCE;     
          }    
    }   

    反射时单例被破坏的解决方法:修改构造器,让它在创建第二个实例的时候抛异常

    public static Singleton INSTANCE = new Singleton();     
    private static volatile  boolean  flag = true;
    private Singleton(){
        if(flag){
        flag = false;   
        }else{
            throw new RuntimeException("The instance  already exists !");
        }
    }

    1、

    2、

    3、

    4、双_锁检测(DCL),JDK1.5之前volatile不能完全避免指令重排序优化,直到1.5才修复。因此JDK1.5之前Java中无法安全地使用DCL来实现单例模式。

    5、

    6、

    枚举类实际上就是一个继承Enum类的类。

    使用枚举单例的写法,我们完全不用考虑序列化和反射的问题。枚举序列化是由jvm保证的,每一个枚举类型和定义的枚举变量在JVM中都是唯一的,在枚举类型的序列化和反序列化上,Java做了特殊的规定:在序列化时Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的并禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法,从而保证了枚举实例的唯一性。

    在单例中,枚举也不是万能的。在android开发中,内存优化是个大块头,而使用枚举时占用的内存常常是静态变量的两倍还多,因此android官方在内存优化方面给出的建议是尽量避免在android中使用enum。

    更多关于枚举可以见:Java小计-枚举

  • 相关阅读:
    一起来构建前端工具链吧~(新建项目)
    我的前端故事----高仿支付宝密码输入框
    我的前端故事----疯狂倒计时(requestAnimationFrame)
    Oracle 导入导出SQL 查看登录用户表个数
    Oracle11g使用exp导出空表
    Spring Boot(二)Application events and listeners
    Spring Boot(一)启动方式
    Android BroadcastReceiver
    钱格式化
    Intellij idea 快键键
  • 原文地址:https://www.cnblogs.com/z-sm/p/4709345.html
Copyright © 2020-2023  润新知