浅谈设计模式之单例模式
单例模式: 保证一个类仅有一个实例,并提供一个访问它的全局访问点, 目的是为了保证在一个进程中,某个类有且仅有一个实例。
要做到这点,必须将构造方法进行私有,防止外部通过构造方法进行实例化该类,在类内部提供方法引用唯一创建的实例。
实现方式
懒汉模式
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
官方不建议使用枚举类实现单例, 因为内存消耗会其他方式多一些 。
总结
单例的实现方式有多种,该怎么选择需要根据实际情况考虑,如果说不考虑多线程则直接使用懒汉模式,简单又节约,如果说考虑多线程则建议使用双重校验法或者静态内部法,或者如果说确保项目中使用单例情况非常少,则可以考虑使用枚举实现。