Java-23个设计模式 Singleton 单例模式

Singleton 

分为懒汉式和饿汉式和登记式,适用于一个类只有一个实例的情况(保证一个类仅有一个实例,并提供一个该实例的全局访问点)

懒汉式:在第一次调用方法的时候会做一个判断,如果实例不存在,则创建一个,如果实例存在,则直接返回。延迟加载,比如配

配置文件,被用到的时候才会加载

饿汉式:一开始就加载了

① 懒汉模式

/**
 *
 * 懒汉模式:第一次被引用时候才被初始化
 */
public class Singleton {

    private final static Object syncLock = new Object();

    // 设立静态变量
    private static volatile Singleton instance = null;

    //让构造函数为 private,这样该类就不会被实例化
    private Singleton(){}

    /**
     * 单线程用
     * 由于多线程下instance可能判断都为空,线程不安全
     */
    //开放一个公有方法,判断是否已经存在实例,有返回,没有新建一个在返回
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }

    /**
     * 并发不高情况可以用
     *
     * 线程安全版本一(空不空都加锁)
     *
     * 弊端:在方法上加锁,但由于要整个方法完成才会释放锁,但锁的代价过高
     * 在高并发时,会有同时很多锁在读代码,锁的代价可能很高
     */
    public synchronized static Singleton getInstance1(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }

    /**
     * 双检查锁,一定要加volatile
     *
     * 线程安全版本二(空才加锁)
     * 锁前不检查:每次都加锁
     * 锁后检查锁后不检查:多线程下可能加好几次锁,创建好几次对象
     * 锁前锁后都检查:多线程下可能加好几次锁,但只会创建一个对象
     *
     * 弊端:内存读写reorder不安全(会导致双检查锁的失效)
     *
     */
    // 同步代码块:
    public static Singleton getInstance2(){
        if(instance == null){
          synchronized (syncLock){
              if(instance == null){
                  instance = new Singleton();
              }
          }
        }
        return instance;
    }
    // synchronized同步块括号中的锁定对象是采用的一个无关的Object类实例,而
    // 不是采用this因为getInstance是一个静态方法,在它内部不能使用未静态的或
    // 者未实例的类对象,因此也可以用下面的方法来实现
    public static Singleton getInstance3(){
        if(instance == null){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }


    /**
     * 正常:先分配内存,再调用构造器,,然后把内存地址给instance
     * reorder:(可能)先分配内存,再把内存地址直接给instance,最后才调用构造器
     * 例:先分配内存0x1234,instance-->0x1234,此时instance!=null。 构造器还没调呢
     *     得到的对象不能用,拿到的只是一个原生的内存,对象只有内存地址、状态不对
     * 解决方法:在实例变量加 volatile : Java的解决方式
     */
}

② 饿汉模式

/**
 * 饿汉模式:加载的时候就被初始化
 */
public class SingletonHungry {

    //设立静态变量,直接创建实例
    private static SingletonHungry instance = new SingletonHungry();

    //让构造函数为 private,这样该类就不会被实例化
    private SingletonHungry(){}

    // 返回对象
    public static SingletonHungry getInstance(){
        return instance;
    }
}

③ 内部类模式

/**
 * 不用加锁也能实现懒加载
 */
public class SingletonInner {
    private SingletonInner(){
        System.out.println("inner singleton");
    }

    /**
     * 内部类
     */
    private static class Inner{
        private static SingletonInner instance = new SingletonInner();
    }
    public SingletonInner getSingletonInner(){
        return Inner.instance;
    }
}

如果说懒汉式是时间换空间,那么饿汉式就是空间换时间
饿汉式虽然节省了时间,但是浪费了空间

场景:在软件系统中,经常有这样一些特殊的类,必须保证它们在系统中只存在一个实例,才能确保它们的逻辑正确性、以及良好的效率

之前的工厂方法和这个相比:工厂模式绕过new是为了避免new带来的松耦合。而Singleton绕过构造器是解决性能的问题。

 

 

你可能感兴趣的:(23个设计模式(学习笔记))