设计模式-单例模式

2019-10-22  本文已影响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来保证全局唯一,这样就可以解决不同类加载器加载类导致的问题。

上一篇下一篇

猜你喜欢

热点阅读