1. 关于单例模式
单例模式是设计模式中最常见也最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局的访问到。
比如在android实际APP 开发中用到的 账号信息对象管理,数据库对象(SQLiteOpenHelper)等都会用到单例模式。下面针对一些例子分析一下我们在开发过程中应用单例模式需要注意的点。
1.1 作用
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1.2 适用场景
- 某个实例对象需要频繁的被访问。
- 确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源。
1.3 条件
- 单例类必须自己创建自己的唯一实例。(构造函数私有化,防止外部程序通过new来构造)。
- 单例类必须给其他对象提供这一实例。(暴露公有静态方法或者通过枚举返回单例类对象)。
2. 单例模式六种方法
2.1 恶汉式
public final class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
- 特点:声明静态对象的时候进行初始化静态对象,以后不再改变,天生是线程安全的。
- 弊端:消耗资源
2.2 懒汉式(线程不安全)
public final class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
- 特点:延迟加载(需要的时候才去加载)
- 弊端:线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。
2.3 懒汉式(线程安全)
public final class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
- 特点:延迟加载(需要的时候才去加载),加入线程锁保证了线程安全。
- 弊端:造成不必要的同步开销。
2.4 双重锁
public final class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null)
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
- 特点:延迟加载,解决了多余的同步,线程安全。两次判空,第一层是为了避免不必要的同步。 第二层是为了在instance为null的情况下创建实例。
2.5 内部类
public final class Singleton {
private static final class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
- 特点:延迟加载,线程安全(java中class加载时互斥的),也减少了内存消耗。
2.6 单元素枚举
public enum Singleton {
instance;
public void doMethod() {
}
}
- 特点:写法简单,线程安全,并且在任何情况下,它都是一个单例。即使是在反序列化的过程,枚举单例也不会重新生成新的实例。
- effective java推荐同时也是目前JDK版本最推荐的做法是单元素枚举。
2.7 注意⚠️
除了枚举方法,其他方法在返回单例时必须抛出 throws ObjectStreamException
,才能保证反序列化不会生成新的对象。