要么改变世界,要么适应世界

浅谈设计模式之单例模式

2020-05-07 12:48:00
249
目录

单例模式: 保证一个类仅有一个实例,并提供一个访问它的全局访问点, 目的是为了保证在一个进程中,某个类有且仅有一个实例。

要做到这点,必须将构造方法进行私有,防止外部通过构造方法进行实例化该类,在类内部提供方法引用唯一创建的实例。

实现方式

懒汉模式

package study.design.singleton;

public class Singleton {
    private static Singleton instance = null;

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

    private Singleton() {
    }
}

这样外部类就不能通过构造方法进行实例化该类,只能通过Singleton.getInstance()的方式进行获取唯一的对象。但是很遗憾,该方式没有考虑多线程的情况,不同的线程同时调用Singleton.getInstance()的时候很有可能会实例化多个对象。因此需要对getInstance()进行加锁

加强版懒汉模式(线程安全)

给加上getInstance(),加上synchronized关键字

package study.design.singleton;

public class Singleton {
    private static Singleton instance = null;

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

    private Singleton() {
    }
}

这样多线程并发执行getInstance()的时候,就只有最早拿到锁的线程能够执行instance = new Singleton(),其他的线程从阻塞队列中下来的时候Singleton已经被实例化了,因此不会执行instance = new Singleton(),从而确保多线程安全。 遗憾的是,由于synchronized的存在,效率很低,在单线程的情景下,完全可以去掉synchronized,为了兼顾效率与性能问题,改进后代码如下:

package study.design.singleton;

public class Singleton {
    private volatile static Singleton instance = null;

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

    private Singleton() {
    }
}

该方法也叫**“双重校验法”**,在instance != null的时候,就不会进行加锁操作,提升了系统性能。

饿汉模式

package study.design.singleton;

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

    public static synchronized Singleton getInstance() {
        return instance;
    }

    private Singleton() {
    }
}

该模式在加载该类的时候直接实例化该类,因此不管用与不用都占着空间,如果项目中有大量单例对象,则可能会浪费大量内存空间

静态内部类加载

package study.design.singleton;

public class Singleton {
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
    }
}

该模式在加载Singleton类的时候不会直接实例化该类,而是调用getInstance()后才实例化Singleton类,做到了懒加载的效果,而且是线程安全的。

借助枚举类

package study.design.singleton;

public enum Singleton {
    INSTANCE;
}

是不是十分的简洁?这种方式解决了以下三个问题:

  • 自由序列化。关于序列化,这里有比较详细的介绍
  • 保证只有一个实例。
  • 线程安全。

引用实例对象的时候只需要:Singleton singleton = Singleton.INSTANCE即可,当然了,我们也可以像常规类一样添加变量和方法:

package study.design.singleton;

public enum Singleton {
    INSTANCE;
    private String name = "INSTANCE";
    public String getName() {
        return name;
    }
}

调用的时候只需Singleton.INSTANCE.getName()

但是在Android平台上面,Android官方不建议使用枚举类实现单例, 因为内存消耗会其他方式多一些 。

总结

单例的实现方式有多种,该怎么选择需要根据实际情况考虑,如果说不考虑多线程则直接使用懒汉模式,简单又节约,如果说考虑多线程则建议使用双重校验法或者静态内部法,或者如果说确保项目中使用单例情况非常少,则可以考虑使用枚举实现。

历史评论
开始评论