单例模式

2018-01-30  本文已影响3人  HWilliamgo
public class Singleton {
    /**
     * 饿汉模式
     * 在类加载时就完成了静态对象的初始化,所以类加载较慢,但获取对象的速度较快
     * 这种方式基于类加载机制,避免了多线程同步问题。
     * 但是由于在类加载的时候就完成了单例对象的实例化,那么如果至始至终从未使用该实例,就造成了内存的浪费
     **/
    private static Singleton instance = new Singleton();
    private Singleton() {

    }
    public static Singleton getInstance() {
        return instance;
    }
}

class Singleton2 {
    /**
     * 懒汉模式
     * 在第一次调用getInstance()方法时对静态对象进行初始化,虽然节约了资源,但是第一次初始化会慢一点
     * 此外,如果多个线程调用此方法,就可能会new 出多个对象,无法工作
     **/
    private static Singleton2 instance;
    private Singleton2() {
    }

    public static Singleton2 getInstance() {
        if (null == instance) {
            instance = new Singleton2();
        }
        return instance;
    }
}

class Singleton3 {
    /**
     * 懒汉模式
     * 可在多线程中正常工作,但是每一个getInstance方法都需要同步,造成了不必要的开销,而且大多时候我们不需要同步
     * 所以不推荐使用该方法**/
    private static Singleton3 instance;
    private Singleton3(){
    }
    public static  synchronized Singleton3 getInstance(){
        if (null==instance){
            instance=new Singleton3();
        }
        return instance;
    }
}

class Singleton4{
/**double check双重检查模式(DCL)
 * 
 * 1:是为了不必要的同步(如果instance已经被初始化过,那么就没必要再同步初始化对象)
 * 2:锁保证了可见性,下面对instance的赋值,在退出同步后,instance的新值对别的线程是立刻可见的。
 * 3:再一次非空判断是非常重要的,如果少了代码行3,其他线程由于依次通过了2,所以会各自创建各自的instance对象,单例模式失效
 * 4:对象初始化,单线程中初始化对象没问题,但是多线程对正在初始化的对象的读写,由于初始化指令的重排序,会造成未完全
 * 初始化就访问对象,造成不可控的问题。
 * 
 * volatile是保证对象初始化的原子性,防止对象分配内存后,还没完全初始化,
 * 而这时引用已经指向对象,同步块结束,另一个线程返回了instance,instance却还没初始化完成。
 *
 * double check过程描述:
 * 比如说有100个线程同时调用getInstance方法,此时全部线程都进入getInstance的1,判断instance都为空之后
 * ,全部线程都进入2,这时只有一个线程能够进入3,假设线程50进入了3,其他线程都暂时阻塞,线程50顺利经过3的判
 * 断之后进入4,那么此时线程50拿到了Singleton4的初次初始化的对象instance,由于instance用了volatile,那么此
 * 时instance的初始化指令不会被重排序,等instance对象完全初始化完之后,退出同步代码块,由于加锁
 * 保证了instance的可见性,其他99条线程均看到了instance的新值,要么不会进入同步代码块,要么进入后立刻跳出
 * 并返回正确的对象。
 **/
    private volatile static Singleton4 instance;
    private Singleton4(){
    }
    public static Singleton4 getInstance(){
        if (instance==null){//1
            synchronized (Singleton4.class){//2
                if (instance==null){//3
                    instance=new Singleton4();//4
                }
            }
        }
        return instance;
    }
}

class Singleton5{
    /**
     * 静态内部类单例模式
     * 第一次加载Singleton5类的时候不会初始化instance,只有调用getInstance方法时,虚拟机加载
     * SingletonHolder,并直接初始化instance.
     * 优点:不仅能保证线程安全(SingletonHolder类只会加载一次,
     * 那么private static final Singleton5 instance=new Singleton5();就只调用一次),而且还能
     * 保证Singleton5类的唯一性**/
    private Singleton5(){
    }
    public static Singleton5 getInstance(){
        return SingletonHolder.instance;
    }
    private static class SingletonHolder{
        private static final Singleton5 instance=new Singleton5();
    }
}

关于内部类和静态内部类何时被加载:加载一个类时,其内部类是否同时被加载?静态内部类单例模式

结论:最好的单例模式是静态内部类单例模式

上一篇下一篇

猜你喜欢

热点阅读