介绍
特点:只有一个实例对象存在(因为被 private 修饰)。
应用场景:
(1)资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。
(2)控制资源的情况下,方便资源之间的互相通信。如线程池等。
实现方法(三种):
名称 | 特点 | 优点 | 缺点 |
---|---|---|---|
饿汉法 | 在加载类的时候就会创建类的单例,并保存在类中 | 代码简单 | 无法延迟加载,线程不安全,可被人使用反射强行调用构造器 |
懒汉法 | 不在系统加载时就创建类的单例,而是在第一次使用实例的时候再创建,为保证线程安全,加 synchronized 关键字修饰 | 延迟加载,加 synchronized 关键字后实现线程安全 | 效率低下,可被人使用反射强行调用构造器 |
双重加锁机制 | 双重判断,先判断单例是否存在,只对不存在的单例进行同步操作 | 延迟加载,JDK 1.5 后线程安全,效率较懒汉高 | 使用 volatile 关键字(修饰的变量不会被本地线程缓存,读写操作直接操作内存决定,会屏蔽虚拟机中的优化代码,运行效率低下)可被人使用反射强行调用构造器 |
静态内部类法 | 将 Singleton 实例放到一个静态内部类中,静态内部类只会加载一下,Singleton 只实例化一次 | 延迟加载和线程安全 | 需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例,可被人使用反射强行调用构造器 |
枚举写法(最优) | 枚举 | 线程安全,防止反射强行调用构造器,自动序列化,防止反序列化时自动创建对象 | Android平台不推荐使用 |
饿汉法
class EHSingleton {
private static EHSingleton ehSingleton = new EHSingleton();
private EHSingleton() {}
public static EHSingleton getSingleton() {
return ehSingleton;
}
}
懒汉法
class LHSingleton {
private static LHSingleton lhSingleton = null;
private LHSingleton() {}
private static synchronized LHSingleton getLhSingleton() {
if (lhSingleton == null) {
lhSingleton = new LHSingleton();
}
return lhSingleton;
}
}
双重加锁机制
class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
静态内部类法
class staticSingleton {
private static class Holder {
private static staticSingleton staticSingleton = new staticSingleton();
}
private staticSingleton() {}
public static staticSingleton getSingleton() {
return Holder.staticSingleton;
}
}
枚举法
enum enumSingleton {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
}
总结:
不同的环境可根据具体情况选择合适的单例模式,但尽量要实现单例的三个要点:
1、保证线程安全
2、延迟加载
3、序列化与反序列化安全
可优选“枚举写法”,JDK 1.5 之前 不适合先选择“双重加锁写法”,Android平台不适宜选择“枚举”。
THE END