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

设计模式-单例模式

作者: 圣村的希望 | 来源:发表于2019-10-22 08:31 被阅读0次

      单例模式是在全局拥有唯一一个实例,这个唯一是有相对性的。有时候需要在系统中拥有一个实例,例如配置信息这些等。单例模式就是保证一个类仅有一个实例,并且提供一个全局访问点。
      单例模式的几种实现方式:

    • 单线程安全简易模式:懒汉模式
    public class Singleton {
    
        private static Singleton instance = null;
        
        private Singleton() {}
        
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

        这个是最容易想到的实现方式,但是往往最容易想到的模型,就会很容易出现问题。这个实现方式在单线程模式下运行没有问题,但是在多线程环境下就会出现线程安全问题。

    • 多线程安全模式:
    public class Singleton {
    
        private static Singleton instance = null;
    
        private Singleton() {}
    
        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

        为了在多线程下并发安全,在方法的前面加上synchronized,这样通过synchronized关键字来保证线程的并发安全。优点是实现方式简单,但是缺点是对所有的读和写都加锁串行化,导致并发效率不高,本来instance已经实例化,getInstance就是一个读操作,这样并不需要加锁,所以接下来进行优化。

    public class Singleton {
        //这里的静态变量要加volatile关键字
        private static volatile Singleton instance = null;
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            //保证了读的无锁划处理,instance不为空直接返回
            if (instance == null) {
                //保证写操作的串行化处理
                synchronized (Singleton.class) {
                    //获取到静态类锁之后,要判断是否前面的线程已经实例化
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

        DCL双重锁实现方式,对静态变量采用volatile关键字来进行修饰,保证了内存可见性和防止指令重排序操作,因为new Singleton()操作不是原子性的,他是在内存中分配空间,然后是实例化等操作,再就是将引用指向对应的内存空间。所以在这个时候可能发生指令重排序,就需要volatile关键字来保证。并且还让其他线程能对instance实例及时感应。

    • 类变量加载时初始化:饿汉模式,线程安全
    public class Singleton {
    
        private static Singleton instance = new Singleton();
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    

        这个是在Singleton类被加载准备阶段就进行实例化,静态类变量和类的静态代码块在类加载准备阶段的时候,他会被jvm搜集起来构成类的构造函数<cinit>然后被执行,所以他是靠jvm来保证线程安全的,实现起来很简单方便,但是他是饿汉模式,在类被加载的时候就被初始化了。

    • 静态内部类的实现方式:懒汉模式,线程安全
    public class Singleton {
    
        private static Singleton instance = new Singleton();
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            return SingletonHolder.instance;
        }
    
        private static class SingletonHolder {
            private static final Singleton instance = new Singleton();
        }
    }
    

        这个其实还是靠jvm在加载类的时候来保证线程安全性以保证全局唯一,只不过他是懒汉模式,还是在getInstance的时候才会被初始化实例。
    单例模式存在的问题:
        这里面的单例是具有相对性的,在java中不同的类加载器在加载同一个类的时候,也会被认为是不同的实例,所以在实现单例模式的时候,不同的类加载器会导致在同一个jvm实例下类的实例并不是唯一的。
        Spring中实现单例的方式是通过容器来实现的,他是通过一个全局的ConcurrentHashMap容器来存放所有类的实例,类的完全路径作为key来保证全局唯一,这样就可以解决不同类加载器加载类导致的问题。

    相关文章

      网友评论

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

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