美文网首页
Java设计模式--单例模式

Java设计模式--单例模式

作者: 留给时光吧 | 来源:发表于2018-04-09 22:31 被阅读0次

    许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为,这时就需要用到单例模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。

    实现单例模式的思路是:一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法);同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

    几种单例模式的实现:

    1.懒汉式

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

    这种实现方法比较简单,但是在多线程环境中并不能保证单例,所以它是线程不安全的,为了应对我们可以加同步锁,如下:

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

    虽然这种方法避免了线程不安全的缺点,但是每次获取单例时都需要加锁再判断是否初始化过,非常耗费性能,所以催生了下面这种方法

    2.Double-Checked Locking

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

    这种实现只有在第一次初始化时才进行加锁,既保证了线程安全,有不耗费太多性能。但是DCL方法并非完美的,主要问题出在这一步:instance = new Singleton();在底层中,这一步会被分解为三步执行:

    • 1.给Singleton的实例分配内存
    • 2.调用Singleton构造函数,初始化成员变量
    • 3.将instance指向分配的地址

    而在JVM虚拟机中,这三步并不能保证顺序执行,比如顺序为1 3 2,虽然最后结果并不会有错,但是在多线程环境下,当执行完第3步时,此时instance并不为空,其余线程在调用getInstance方法时,不会进行初始化,直接拿走instance对象导致出错。不过在jdk 1.5之后 ,如果用volatile关键字修饰instance,就可以避免这种可能性较低但是有时会出现的错误,当然这样也会导致性能的下降。

    3.饿汉式
    懒汉式的优点是懒加载,只有在用到的时候才初始化,避免资源浪费,但会导致线程安全问题。所以又有一种比较极端的饿汉式方法来实现单例:

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

    这个主要是借助类加载机制,避免了多线程问题,但是显而易见,不管这个类是否使用,都会创建对象,造成了资源的大量浪费。

    4.静态内部类方法

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

    这个主要是弥补了饿汉式和懒汉式的缺点,首先不使用锁机制,保证了性能,同时也利用类加载机制,保证了线程安全问题,最后他也属于延迟加载的,避免了资源的浪费,是一种成熟的单例实现方法。

    5.枚举单例
    再jdk 1.5之后,又给我们提供了一种新的实现单例的思路 -- 枚举。这也是 Effective Java书中提倡的。

    public enum Singleton {
        INSTANCE;
    }
    

    枚举类特点就是可以和普通类一样拥有成员变量和方法,同时默认是线程安全的。它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,也防止通过反射调用私有构造。

    相关文章

      网友评论

          本文标题:Java设计模式--单例模式

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