• 面试题2:单例模式Singleton


    首先,单例模式使类在程序生命周期的任何时刻都只有一个实例,

    然后,单例的构造函数是私有的,外部程序如果想要访问这个单例类的话,

    必须通过 getInstance()来请求(注意是请求)得到这个单例类的实例。

    1、饿汉式

    package singleton;
    /**
     * 单例模式保证一个类仅有一个实例,同时这个类还必须提供一个访问该类的全局访问点。
     * 
     * 饿汉式:new之后,便创建对象,以后不再改变,此种方式天生线程安全的
     */
    public class SingletonDemo1 {
    
        //定义一个私有的静态全局变量来保存该类的唯一实例 
        private static SingletonDemo1 instance=new SingletonDemo1();
        //构造函数必须是私有的,这样在外部便无法使用 new 来创建该类的实例 
        private SingletonDemo1(){
        }
        
        //定义一个全局访问点 ,设置为静态方法 ,则在类的外部便无需实例化就可以调用该方法 
        public static SingletonDemo1 getInstance(){
            return instance;
        }
        
    }

     2、懒汉式

    package singleton;
    /**
     * 懒汉式:在需要的时候创建对象
     * if()判断,保证线程的安全性 *
    @author Don * */ public class SingletonDemo2 { private static SingletonDemo2 instance; private SingletonDemo2(){ } //ͬ多线程访问时,当有一个线程访问时就加锁,防止产生多个实例 public static synchronized SingletonDemo2 getInstance(){ if(instance==null){ instance=new SingletonDemo2(); } return instance; } }

     3、静态内部类(结合了前两种的优势)

    package singleton;
    /**
     * 静态内部类:兼备了高效调用和延迟加载的优势
     * @author Don
     *
     */
    public class SingletonDemo3 {
    
        //只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。instance是static final类型
        //保证了内存中只有这样一个实例存在,而且只能被赋值一次,从而保证了线程安全性
        private static class SingletonClassInstance{
            private static final SingletonDemo3 instance=new SingletonDemo3();
        }
        private SingletonDemo3(){
        }
        
        public static  SingletonDemo3 getInstance(){
            return SingletonClassInstance.instance;
        }
        
    }

    饿汉式和懒汉式区别:

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

     

    1、线程安全:

    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。



    2、资源加载和性能:

      饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    至于1、2、3这三种实现又有些区别,

    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

     

    什么是线程安全?

      如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

  • 相关阅读:
    pgloader-pg迁移神器
    PostgreSQL备份工具-pg_probackup
    5、pgpool-II高可用性(一)数据库的高可用性
    4、pgpool-II 流复制模式
    3、pgpool-II 内置复制模式
    pgpool-II 的配置
    pgpool-II安装
    PG时间相减
    PostgreSQL 流复制解惑
    PostgreSQL改造非分区表为分区表
  • 原文地址:https://www.cnblogs.com/Donnnnnn/p/5720220.html
Copyright © 2020-2023  润新知