美文网首页
单例模式

单例模式

作者: HWilliamgo | 来源:发表于2018-01-30 17:47 被阅读3次
    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();
        }
    }
    
    

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

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

    相关文章

      网友评论

          本文标题:单例模式

          本文链接:https://www.haomeiwen.com/subject/xjjczxtx.html