介绍
单例模式可以说是最简单的创建型模式了,面试的时候大部分会以此作为面试题。但相对来说,它挺复杂的,针对于不同情形,有不一样的实现方式。如经典的线程不安全懒汉式,线程安全懒汉式,饿汉式,双检锁/双重校验锁,静态内部类,枚举
定义
在某些场合,出于节约资源等原因,需要只保证一个类只有一个实例化对象。如线程池,数据库连接,缓存,日志对象,打印机等等
优点
- 节省资源,减少内存开销,不用频繁的创建和销毁实例
- 避免对资源的多重占用
缺点
- 没有接口,不能继承。因为构造器是私有的,不能被实例化。
- 与单一职责原则冲突。正常一个类只需要关心自己的内部逻辑(一项职责),不需要关心外部如何实例化它(又一项职责)
核心思想
- 限制构造器的访问范围,设置为private
- 只能通过一个静态方法来返回实例,且实例为静态的
单例模式的实现方式
线程不安全懒汉式
该模式支持Lazy instantiaze(延迟实例化),但该方式线程不安全,不支持多线程
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
线程安全懒汉式
该模式支持Lazy instantiaze(延迟实例化),且该方式线程安全,支持多线程,与线程不安全的唯一区别是获取实例方法加锁,添加上了synchronized关键字
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率,因为加锁代表访问该方法需要同步,必定会产生相对的堵塞。而且我们只需要在第一次执行该方法的时候需要同步,因为第二次的话,该实例对象将不会是null。
应用场景:
- getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
饿汉式
该模式不支持Lazy instantiaze(延迟实例化),但该方式线程安全,支持多线程。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
优点:由于classloder(类加载器)的原因,避免了多线程的同步问题,没有加锁,效率会提高
缺点: 没有延迟实例化机制,在类加载的时候就初始化,浪费内存
应用场景:
这是最常用的单例模式实例方式,但是容易产生垃圾对象。这是一种急切实例化的方式,不再是延迟实例化。
双检锁/双重校验锁(DCL,即 double-checked locking)
该模式支持Lazy instantiaze(延迟实例化),但该方式线程安全,支持多线程,且能保持高性能。
为什么叫双重检查?因为在检查实例化对象是否为null后,进入了同步代码块,然后在代码块中又进行了一次检查。
与线程安全懒汉式不同点:
- 实例化变量被设置为volatile
- 不再是同步方法,而是在进行第一次校验之后,进入同步代码块,锁定实例化类
- 在同步代码块中还要进行第二次校验才能被实例化
public class Singleton {
private volatile static Singleton singleton;
private Singleton(){}
public Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类单例模式
该模式支持Lazy instantiaze(延迟实例化),且该方式线程安全,支持多线程,且能保持高性能。
其核心是静态内部类,静态保证了线程安全,内部类保证了懒加载
public class Singleton{
private Singleton{}
private static class SingletonHolder{
private static Singleton instance =new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
枚举单例模式
该模式支持延迟加载,且线程安全,容易理解
它定义一个枚举的元素,它就代表了Singleton的一个实例,是最简单的单例
public enum Singleton{
uniqueInstance;
}