单例模式

一、饿汉模式

public class SingleTon{
    private static INSTANCE = new SingleTon();
    private SingleTon(){
    }
    public static SingleTon getSingleTon(){
        return INSTANCE;
    }
}

总结:用空间换时间 在一开始就实例化,会稍微浪费点内存,但是优点是不用关心多线程问题

二、懒汉模式

public class SingleTon{
    private static INSTANCE = null;
    private SingleTon(){}
    public static SingleTon getSingleTon(){
        if(INSTANCE == null){
            INSTANCE = new SingleTon();
        }
        return INSTANCE;
    }
}

总结 用时间换空间,在调用getSingleTon时才会真正创建实例,但是缺点是多线程时,无法保证是真正的单例。

三、双重锁懒汉模式

public class SingleTon{
    private static INSTANCE = null;
    private SingleTon(){}
    public static getSingleTon(){
        synchronized(SingleTon.class){
            if(INSTANCE == null){
                INSTANCE = new SingleTon();
            }
        }
        return INSTANCE;
    }
}

总结 DCL模式,只有在对象需要被使用时才创建,加了synchronized后比懒汉模式更安全 多线程情况下也没问题。这样既可以节约内存空间,又可以保证线程安全。但是由于jvm存在乱序执行功能,DCL也会出现线程不安全的情况

INSTANCE = new SingleTon();

这个步骤,其实在jvm里面的执行分为三步:

  1. 在堆内存开辟内存空间
  2. 在堆内存中实例化SingleTon里面的各个参数
  3. 把对象指向堆内存空间 由于jvm乱序功能,所以可能2还没执行时,就先执行了3,如果此时再被切换到线程B上,由于执行了3,INSTANCE已经非空了,会被直接拿出来用,这样就会出现异常。 解决:定义为
private volatile static SingleTon INSTANCE = null;

四、静态内部类模式

public class SingleTon{
    private SingleTon(){}
    private static class SingleTonHoler{
        private static SingleTon INSTANCE = new SingleTon();
    }
    public function SingleTon getInstance(){
        return SingleTonHoler.INSTANCE;
    }
}

总结:静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故不占内存。即当SingleTon第一次被加载时,并不需要去加载SingleTonHoler,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

posted on 2019-12-31 17:06  稀饭里的米  阅读(173)  评论(0编辑  收藏  举报