-
单例模式:
- 根据单例模式的设计思想,就是想保证类只有一个实例即只有一个对象。而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源等。 一般出现的场景是:
- 1、资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如记录系统日志文件,共享配置文件的读取。
- 2、控制资源的情况下,方便资源之间的互相通信。如线程池,数据库连接池等。 实现方法很多种:
- 1、饿汉式:
public class SingletonTest {
private static SingletonTest instance = new SingletonTest();
//私有构造方法 只能通过 公有方法 getInstance 获得实例
private SingletonTest(){}
public static SingletonTest getInstance() {
return instance;
}
}
饿汉式在类加载时静态变量 instance 就进行初始化。资源利用率不好。
2、懒汉式:
public class SingletonTest {
private static SingletonTest instance = null;
private SingletonTest(){}
//synchronized 保证线程同步,线程安全
public static synchronized SingletonTest getInstance() {
if (instance == null) {
instance = new SingletonTest();
}
return instance;
}
}
懒汉式线程安全,在类加载时并不进行初始化,只是在调用方法getInseance() 时会进行初始化,实现了延迟加载(lazy load)。但是也就是因为只要调用 getInstance() 就会都需要进行 线程锁定判断,进行初始化资源,降低系统的性能,特别是多线程,高并发的状态下。
3、双重校验索锁定(懒汉式的改进):
双重校验是懒汉式的改进,因为当同时有2个进程a,b 调用getInstance(),当a进入 线程锁定,创建对象,b只能等a 执行完,退出来,才能进入进程同步块。但是还是不知道 a 创建对象没有。可能就会出现创建了2个对象。
public class SingletonTest {
//被volatile修饰的成员变量可以确保多个线程都能够正确处理
private volatile static SingletonTest instance = null;
private SingletonTest(){}
public static synchronized SingletonTest getInstance() {
if (instance == null) { //第一次校验
synchronized (SingletonTest.class) {
if (instance == null) { //第二次校验
instance = new SingletonTest();
}
}
}
}
}
这样还是有一个问题,就是使用volatile,由于volatile关键字会屏蔽Java虚拟机所做的一些代码的优化,可能会降低系统运行的效率。
4、静态内部类:
静态内部类是一个更好的实现方式,通过内部类创建对象,外部类进行返回。
public class SingletonTest {
private static class HolderSingleton {
private static final SingletonTest INSTANCE = new SingletonTest();
}
private Singleton (){}
public static final SingletonTest getInstance() {
return HolderSingleton .INSTANCE;
}
}
类加载时不会实例化,只有调用 内部类的 getInstance() 才会实例化,由于是静态的实例,所以创建的实例是唯一的,而且是线程安全的。
其实这就是 IoDH (Initialization Demand Holder) 技术。既可以实现延迟加载,又可以保证线程安全,不影响系统性能。
5、单个元素枚举类型:
public enum SingletonTest {
INSTANCE;
private TestObject obj;
SingletonTest() {
obj = new TestObject();
}
private Singleton getInstance() {
return obj;
}
}
根据枚举的唯一性,在初始化的时候,构造器就生成obj,根据唯一的instance 去获取obj 也一定是唯一的,这个单例模式实现的很巧妙。
其实这是Joshua Bloch(Effective Java作者)的思路,这种方法更加简洁,无偿提供序列化机制,绝对防止多次序列化,即使面对复杂的序列化或反射攻击的时候。只是这只能是针对 java 1.5 以上版本的。 因为 java 1.5 才有枚举enum 新特性。
用 Joshua 的话说“单元素的枚举类型已成为实现 Singleton 的最佳方法。
共勉。