单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点
在这里将从以下几方面介绍
- 实现方式
- 各种实现方式的优缺点
- 总结
实现方式
- 饿汉单例模式
//private的构造函数用于避免外界直接使用new来实例化对象
private SingletonSample() {}
private static final SingletonSample INSTANCE = new SingletonSample();
public static SingletonSample newIntent() {
return INSTANCE;
}
- 懒汉单例模式
//private的构造函数用于避免外界直接使用new来实例化对象
private SingletonSample() {}
private static SingletonSample instance_b;
public static synchronized SingletonSample getInstanceB() {
if (instance_b == null) {
instance_b = new SingletonSample();
}
return instance_b;
}
- 双重校验锁
//private的构造函数用于避免外界直接使用new来实例化对象
private SingletonSample() {}
private static SingletonSample instance_c;
public static SingletonSample getInstanceC() {
if (instance_c == null) {
synchronized (SingletonSample.class) {
instance_c = new SingletonSample();
}
}
return instance_c; }
- 静态内部类
//private的构造函数用于避免外界直接使用new来实例化对象
private SingletonSample() {}
public static SingletonSample getInstanceD() {
return SingleHolder.instance_d;
}
private static class SingleHolder {
private static final SingletonSample instance_d = new SingletonSample();
}
- 枚举
public enum SingletonSamples {
INSTANCE;
public void toPlayMusic() {
}
}
public static void main(String[] args) {
SingletonSamples.INSTANCE.toPlayMusic();
}
以上实现方式的优缺点
实现方式 | 优点 | 缺点 |
---|---|---|
饿汉 | 在类加载的同时已经创建好一个静态对象 | 不具备lazyloading |
懒汉 | 资源利用率高,具备很好的lazy loading | 多数情况下可能并不需要同步 |
双重校验锁 | 资源利用率高, | 由于java内存模型一些原因偶尔失败,不过在jdk1.5开始这个问题已经解决,由于volatile关键字屏蔽了虚拟机中一些必要的代码优化,所以运行效率并不是很高,因此建议没有特别的需要不要使用。双重检验锁方式的单例不建议大量使用,根据情况决定 |
静态内部类 | 资源利用率高,线程安全 | ----- |
枚举 | 线程安全, 调用效率高(从Java1.5开始支持;无偿提供序列化机制,绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候) | ----- |
总结
根据以上的优缺点比较,对于单例模式,我们更多的应该使用静态内部类和单元素枚举实现单例模式,Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,个人认为可能是jdk1.5引入的枚举,这种方式在我们的工作中用的并不是很多.
需要注意以下两个问题
- 如果Singleton实现了java.io.Serializable接口,那么它就能够进行序列化和反序列化,当它在返序列化的时候会产生新的对象,解决方法如下:
private Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
- 如何防止反射获取多个对象
private SingletonSamples() {
if (INSTANCE != null) {
throw new RuntimeException();
}
}