开发中或多或少都会用到单例模式,写法有很多种,这里收集整理了下:
1、懒汉式:
1)把构造方法定义成private的,不让外面new的方式生成实例;
2)提供一个static方法供外部调用,在这个方法里面返回实例;
public class SingleInstance {
private static SingleInstance singleInstance;
private SingleInstance() {
}
public static SingleInstance getInstance(){
if (null == singleInstance){
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
只有在需要的时候,调用getInstance()方法才创建实例;
2、饿汉式
第一次调用的时候先生成实例,在通过getInstances()方法返回,是一种线程安全的写法;
public class SingleInstance {
private static final SingleInstance INSTANCE = new SingleInstance();
private SingleInstance() {
}
public static SingleInstance getInstance(){
return INSTANCE;
}
}
3、线程安全(synchronized)
懒汉式有个缺陷,就是无法处理并发的情况,试想有这样一个场景:
A线程第一次调用getInstance()获取一个实例,singleInstance为空,创建一个实例;
B线程第一次调用getInstance(),如果此时实例还未创建,singleInstance为空,就会在创建一个实例;
这样就会生成两个实例,修改下懒汉式的写法:
public class SingleInstance {
private static SingleInstance singleInstance;
private SingleInstance() {
}
public synchronized static SingleInstance getInstance(){
if (null == singleInstance){
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
在方法面前加上synchronized 同步锁保证线程安全。
4、双重检验锁
上面的写法优化一下,可以改写成这样:
public class SingleInstance {
private static volatile SingleInstance singleInstance;
private SingleInstance() {
}
public static SingleInstance getInstance(){
if (null == singleInstance){
synchronized (SingleInstance.class){
if (null == singleInstance){
singleInstance = new SingleInstance();
}
}
}
return singleInstance;
}
}
多了一个判断,是不是看起来比上面那种写法逼格更高了;好吧这里简单解释下:
当有多线程调用getInstance()的时候,先判断实例是否为空,如果不为空则直接返回,没后面什么事了;
如果为空则进入synchronized 语句块,下面过程就和上面写法一样了;
至于volatile关键字是让在编译的时候按顺序执行。
5、静态内部类的方式
上面的饿汉式也有个弊端,如果我需要在外部调用SingleInstance的一个全局变量,在类加载时,会先初始化生成实例,这好像并不是我们想要的,好吧,那就换一种写法:
public class SingleInstance {
private SingleInstance() {
}
private static class SingleHolder{
private static final SingleInstance INSTANCE = new SingleInstance();
}
public static SingleInstance getInstance(){
return SingleHolder.INSTANCE;
}
}
这样就只在调用getInstance()方法的时候才去实例化。
6、反序列化的方式
这种写法是避免别人通过反射的方式来获取实例:
public class SingleInstance implements Serializable{
private SingleInstance() {
}
private static class SingleHolder{
private static final SingleInstance INSTANCE = new SingleInstance();
}
public static SingleInstance getInstance(){
return SingleHolder.INSTANCE;
}
private Object readResolve(){
return SingleHolder.INSTANCE;
}
}
实现Serializable接口,增加readResolve()方法,这里复制一波解释吧:”反序列化机制:在反序列化的时候会判断如果实现了serializable 或者 externalizable接口的类中又包含readResolve()方法的话,会直接调用readResolve()方法来获取实例。”
7、枚举方式
通过反序列化的方式只是避免别人通过反射的方式获取实例,但并不能彻底阻止;
public enum SingleInstance{
INSTANCE;
//... ...
}
因为枚举的特点,只会有一个实例,同时保证了线程安全、反射安全和反序列化安全。
以上总结基本就这些了,其中3、4可以看做是懒汉式的衍生体吧,5可以看做是饿汉式的衍生体;具体使用哪个自己考量了吧,我个人用的多的还是4和5,如果获取实例的时候需要传值选用第四种。