目录
一、单例模式的作用和使用场景
单例模式是设计模式中最常见的的一种设计模式,保证了一个类只有一个实例存在并且能够全局访问到。常见的使用场景有:应用中某个实例对象需要被频繁的访问;应用每次启动都只会存在一个实例,如账号系统、数据库对象(SQLiteOpenHelper)等。
二、五种单例模式
2.1 饿汉模式
public class SingletonStarving {
//直接初始化静态变量,确保线程安全
private static final SingletonStarving mInstance= new SingletonStarving();
private SingletonStarving() {
}
public static SingletonStarving getInstance(){
return mInstance;
}
}
- 构造函数使用private修饰,外部无法访问
- 在声明对象时初始化
- static关键字修饰静态变量,使内存中只有一份数据
- final关键字保证只初始化一次
2.2 懒汉模式
public class SingletonSlacker {
private static SingletonSlacker mInstance;
private SingletonSlacker() {
}
public static synchronized SingletonSlacker getInstance() {
if (mInstance == null) {
mInstance = new SingletonSlacker();
}
return mInstance;
}
}
- 构造函数使用private修饰,外部无法访问
- 使用的时候要调用getInstance的时候才初始化
- static关键字修饰,静态变量在内存中只有一份数据
- synchronized线程安全,在多线程下单例的唯一性
- 缺点是:每次调用都会getInstance一次
2.3 双重检查加锁方式
public class Singleton {
//volatile关键字确保多线程下正确处理Singleton
private volatile static Singleton mInstance;
private Singleton() {
}
public static Singleton getInstance() {
//第一次判空
if (mInstance == null) {
//进入同步区域
synchronized (Singleton.class) {
//第二次判空
if (mInstance == null) {
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
- 构造函数使用private修饰,外部无法访问
- 在使用的时候调用getInstance进行初始化
- static关键字修饰
- synchronized线程安全,多线程情况下的唯一性
- 两次判空避免多次同步
2.4 静态内部类方式
public class SingletonInternalClass {
private SingletonInternalClass() {
}
public static SingletonInternalClass getInstance() {
return SingletonInternalClassHolder.instance;
}
private static class SingletonInternalClassHolder {
private static final SingletonInternalClass instance = new SingletonInternalClass();
}
}
- 构造函数使用private修饰外部无法访问
- 使用的时候调用getInstance进行初始化
- 调用getInstance才去加载SingletionInternalClassHolder类,确保线程安全。
2.5 枚举单例模式
public class User {
//私有化构造函数
private User(){}
//定义一个静态枚举类
static enum SingletonEnum{
//创建一个枚举对象
INSTANCE;
private User user;
//私有化枚举的构造函数
private SingletonEnum(){
user = new User();
}
public User getInstance(){
return user;
}
}
//对外暴露一个获取User对象的静态方法
public static User getInstance(){
return SingletonEnum.INSTANCE.getInstance();
}
}
总结
1.饿汉式:类初始化的时候会立即加载该对象,线程安全调用效率高。缺点是造成内存浪费。
2.懒汉式:只有使用的时候才去创建对象,具备懒加载的功能。缺点:线程不安全。
3.双重检查加锁的方式:只在使用时加载,节省资源,缺点:存在线程安全问题
3.静态内部类方式:结合了懒汉模式和饿汉模式的各自优点,需要时加载而且线程安全
4.枚举类型的单例:使用枚举实现单例模式,实现简单调用效率高。枚举本身就是单例,由JVM从根本上提供保障,避免反射和反序列化的漏洞,缺点时没有延迟加载。