面试题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时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

 

什么是线程安全?

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

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

原文地址:https://www.cnblogs.com/Donnnnnn/p/5720220.html