美文网首页java基础
单例模式的基本实现方式

单例模式的基本实现方式

作者: java面试收割机 | 来源:发表于2017-09-28 10:51 被阅读0次

    单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间;如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。

    基本实现方式:

    1、饿汉模式

    public class Singleton{  
        private static Singleton instance = new Singleton();  
        private Singleton(){}  
        public static Singleton newInstance(){  
            return instance;  
        }  
    }  
    

    类的构造函数定义为private的,保证其他类不能通过构造方法实例化此类,然后提供了一个静态方法把实例返回给调用者。饿汉模式是最简单的一种实现方式,饿汉模式在类加载的时候就对实例进行创建,实例在整个程序周期都存在。它的好处是只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。它的缺点也很明显,即使这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。对小对象比较实用,大对象会比较耗资源。

    2、懒汉模式

    public class Singleton{  
        private static Singleton instance = null;  
        private Singleton(){}  
        public static Singleton newInstance(){  
            if(null == instance){  
                instance = new Singleton();  
            }  
            return instance;  
        }  
    }  
    

    懒汉模式中,实例是在需要的时候才去创建的,如果实例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。但是该实现方式有并发问题。

    3、加同步锁的懒汉模式

    public class Singleton{  
        private static Singleton instance = null;  
        private Singleton(){}  
        public static synchronized Singleton newInstance(){  
            if(null == instance){  
                instance = new Singleton();  
            }  
            return instance;  
        }  
    }  
    

    该方式保证了线程安全,但是如果该实例实用的频繁会存在效率问题。

    4、加双重校验的懒汉模式

    public class Singleton {  
        private static Singleton instance = null;  
        private Singleton(){}  
        public static Singleton getInstance() {  
            if (instance == null) {  
                synchronized (Singleton.class) {  
                    if (instance == null) { 
                        instance = new Singleton();  
                    }  
                }  
            }  
            return instance;  
        }  
    }  
    

    改方式在外面再加了一层null判断,避免每次都走同步锁,既保证了线程安全,又提高了执行效率。但是如果用到指令重排的话,也会存在问题

    由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。

    为了解决这个问题,就需要用到volatile关键字。volatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的,从而避免了上面说到的问题。

    5、volatile的双重枷锁

    public class Singleton {  
        private static volatile Singleton instance = null;  
        private Singleton(){}  
        public static Singleton getInstance() {  
            if (instance == null) {  
                synchronized (Singleton.class) {  
                    if (instance == null) {  
                        instance = new Singleton();  
                    }  
                }  
            }  
            return instance;  
        }  
    }  
    

    6、静态内部类实现方式

    public class Singleton{  
        private static class SingletonHolder{  
            public static Singleton instance = new Singleton();  
        }  
        private Singleton(){}  
        public static Singleton newInstance(){  
            return SingletonHolder.instance;  
        }  
    }  
    

    它与饿汉模式一样,也是利用了类加载机制,因此不存在多线程并发的问题。不一样的是,它是在内部类里面去创建对象实例。这样的话,只要应用中不使用内部类,JVM就不会去加载这个单例类,也就不会创建单例对象,从而实现懒汉式的延迟加载,同时保证延迟加载和线程安全

    总的来说 最后两种是最优的实现方式。

    相关文章

      网友评论

        本文标题:单例模式的基本实现方式

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