• 单例模式


    一、懒汉式

    public class Singleton {
        private volatile static Singleton instance; //声明成 volatile
        private Singleton (){}
    
        public static Singleton getSingleton() {
            if (instance == null) {                         
                synchronized (Singleton.class) {
                    if (instance == null) {       
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    
    }
    

      使用DCL进行锁的双重检查。这里的volatile是亮点:设置内存屏障,防止instance未初始化完就被泄漏出来。(volatile语义不清楚的可能参考:Java并发编程:volatile关键字解析)

    二、饿汉式

    public class Singleton{
        //类加载时就初始化
        private static final Singleton instance = new Singleton();
    
        private Singleton(){}
    
        public static Singleton getInstance(){
            return instance;
        }
    }
    

      这种写法简单明了,也线程安全。缺点是它不是一种懒加载模式(lazy initialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。

    三、静态内部类

    public class Singleton {  
        private static class SingletonHolder {  
            private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
            return SingletonHolder.INSTANCE; 
        }  
    }
    

      这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

    四、总结

      一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类。

      

    4.1 反射

    但以上几种方式都无法保证反射出来还是单例。可以通过如下代码验证:

    //获得构造器
    Constructor con = Singleton.class.getDeclaredConstructor();
    //设置为可访问
    con.setAccessible(true);
    //构造两个不同的对象
    Singleton singleton1 = (Singleton)con.newInstance();
    Singleton singleton2 = (Singleton)con.newInstance();
    //验证是否是不同对象
    System.out.println(singleton1.equals(singleton2));
    

      网上流传一种方法,可以利用enum!有了enum语法糖,JVM会阻止反射获取枚举类的私有构造方法。

    public enum SingletonEnum {
        INSTANCE;
    }
    

      

    4.2 反序列化

    反序列化的时候,JVM会根据序列化生成的内容构造新的对象,对于实现了Serializable的单例类来说,这相当于开放了构造方法。为了保证单例类实例的唯一性,我们需要重写resolveObject方法。

    resolveObject:反序列化时替换反序列化出的对象,反序列化出来的对象被立即丢弃。此方法在readeObject后调用。readResolve常用来反序列单例类,保证单例类的唯一性。

    /**
     * 在反序列化的时候被调用
     * @return 返回根据字节码创建的新对象
     * @throws ObjectStreamException
     */
    private Object readResolve()throws ObjectStreamException {
        return instance;
    }
    
    

      

    参考:

  • 相关阅读:
    【转】Jmeter:图形界面压力测试工具
    【转】WINSOCKET客户端编程以及JMETER外部调用
    【转】Jenkins+Ant+Jmeter自动化性能测试平台
    【转】在Linux下使用Jmeter执行测试任务
    【转】Jmeter之GUI运行原理
    初识Python
    苹果广告1984
    Think different
    JD IPO address by liuqiangdong
    iPhone4 offical AD
  • 原文地址:https://www.cnblogs.com/waterystone/p/5546312.html
Copyright © 2020-2023  润新知