单例模式

2020-04-04  本文已影响0人  AD刘涛

单例模式适用场景

  1. 无状态的工具类:比如日志工具类,不管是在哪里使用,我们需要的只是它帮我们记录日志信息,除此之外,并不需要在它的实例对象上存储任何状态,这时候我们只需要一个实例对象即可。
  2. 全局信息类:比如我们在一个类上记录网站的访问次数,我们不希望有的访问被记录在对象A上,有的却记录在对象B上,这时候我们就希望这个类成为单例。

饿汉式

第一种实现方式
package singleton;

/**
 * 描述: 饿汉式(静态常量)[可用,线程安全]
 * 从代码中我们看到,类的构造函数定义为private的,保证其他类不能实例化此类,然后提供了一个静态实例并返回给调用者。
 * 其实现简单,并且在类加载的时候就对实例进行创建,实例在整个程序周期都存在。
 *
 * 优点:  只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。
 * 缺点:  即使这个单例没有用到也会被创建,而且在类加载之后就被创建,这样一来就存在着内存浪费。
 * 适用于:这种实现方式适合占用内存比较小的单例对象,且在初始化时就会被用到的情况。但是,如果单例占用的内存比较    大,或单例只是在某个特定场景下才会用到,使用饿汉模式就不合适了,这时候就需要用到懒汉模式进行延迟加载。
 */
public class Singleton1   {

    private final static Singleton1 instance = new Singleton1();

    private Singleton1(){

    }

    public static Singleton1 getInstance(){
        return instance;
    }
}

第二种实现方式
/**
 * 描述:     饿汉式(静态代码块)(可用,与上个案例实现原理基本相似,也是线程安全)
 *
 */
public class Singleton2 {

    private final static Singleton2 INSTANCE;

    static {
        INSTANCE = new Singleton2();
    }

    private Singleton2() {
    }

    public static Singleton2 getInstance() {
        return INSTANCE;
    }
}

懒汉式

第一种实现方式
package singleton;

/**
 * 描述:     懒汉式(线程不安全)
 *
 * 优点:懒汉模式中单例是在需要的时候才去创建的,如果单例已经创建,再次调用获取接口将不会重新创建新的对象,而是直接返回之前创建的对象。
 * 缺点:但是这里的懒汉模式并没有考虑线程安全问题,在多个线程可能会并发调用它的getInstance()方法,导致创建多个实例,因此需要加锁解决线程同步问题
 * 适用于:如果某个单例使用的次数少,并且创建单例消耗的资源较多,那么就需要实现单例的按需创建,这个时候使用懒汉模式就是一个不错的选择。
 */
public class Singleton3 {

    private static Singleton3 instance;

    private Singleton3() {

    }

    // 在获取类对象的时候进行初始化,
    // 但是这里的懒汉模式并没有考虑线程安全问题,
    // 在多线程环境下可能会并发调用它的getInstance()方法,导致创建多个实例
    public static Singleton3 getInstance() {
      // 如果多线程并行if (instance == null)这行代码,则会多次创建instance
        if (instance == null) {
            instance = new Singleton3();
        }
        return instance;
    }
}

第二种实现方式
package singleton;

/**
 * 描述:     懒汉式(线程安全)(不推荐)
 */
public class Singleton4 {

    private static Singleton4 instance;

    private Singleton4() {

    }

    // 由于synchronized关键字的使用会造成该方法效率极低,因此不推荐使用
    public synchronized static Singleton4 getInstance() {
        if (instance == null) {
            instance = new Singleton4();
        }
        return instance;
    }
}

第三种实现方式
package singleton;

/**
 * 描述:     懒汉式(线程不安全)(不推荐)
 */
public class Singleton5 {

    private static Singleton5 instance;

    private Singleton5() {

    }

    // 事实上,该方法依旧是线程不安全。why?原因是当多个线程走到 "if (instance == null)"这里后,
    // 会造成线程阻塞。但第一个线程运行结束后,释放锁后,第二个线程依旧会进入该方法,依旧会创建新的对象。
    // 所以即使加入synchronized方法,线程依旧不安全。
    public static Singleton5 getInstance() {
        if (instance == null) {
            synchronized (Singleton5.class) {
                instance = new Singleton5();
            }
        }
        return instance;
    }
}

第四种实现方式
package singleton;

/**
 * 描述:     双重检查(推荐使用)
 */
public class Singleton6 {
    /**
     *  之所以使用volatile关键字是为了阻止重排序,以及可见性,原因是创建对象并非原子操作。
     *  因为创建对象包含3个步骤:1. 新建一个空的Person对象。2. 把这个对象的地址指向引用。
     *  3.执行Person的构造函数。
     */
    private volatile static Singleton6 instance;

    private Singleton6() {

    }

    /**
     *  该方法线程安全。why?原因是当多个线程走到 "if (instance == null)"这里后,
     *  会造成线程阻塞。但第一个线程运行结束后,释放锁后,第二个线程依旧会进入该方法,
     *  一旦线程走到synchronized代码段后都会再次判断实例是否创建,所以线程安全。
     */
    public static Singleton6 getInstance() {
        if (instance == null) {
            synchronized (Singleton6.class) {
                // 一旦线程走到了都会判断实例是否创建
                if (instance == null) {
                    instance = new Singleton6();
                }
            }
        }
        return instance;
    }
}

上一篇下一篇

猜你喜欢

热点阅读