比较常见的单例模式有五种
- 1:饿汉式(类加载的时候即创建实例)
- 2:懒汉式(在需要的时候才创建实例)
- 3:静态内部类单例 (推荐)
- 4:双重锁单例
- 5:枚举单例
下面就直接上代码,通过代码可以更加直观地看出各种实现方式的不同
一:饿汉式
public class AnthorEhan {
//或者放在静态代码块里面
private static final AnthorEhan instance=new AnthorEhan();
//饿汉,线程安全,因为你还没有调用getInstance,就已经创建
//了对象,在加载到内存时就已经创建好了。但是唯一缺点就是,如果实例创建依赖
//其他参数或配置的话,还是需要动态创建,这种加载创建就不能满足了。
public static AnthorEhan getInstance(){
return instance;
}
}
二:懒汉式
public class LanHan {
//这样写线程安全,就是效率不高,因为同时只能有一个线程调用getInstance()方法
// 但实际上,我们认为只有在程序中第一次调用才需要保证同时只能有一个线程调用
public static LanHan instance;
public static synchronized LanHan getInstance(){
if(instance==null){
instance = new LanHan();
}
return instance;
}
}
三:静态内部类
public class Effective {
// 推荐单例模式。懒汉/线程安全/性能保障
//另外还有通过枚举实现单例也是推荐
private static class EffectiveHolder{
private static final Effective INSTANCE=new Effective();
}
public static Effective getInstance(){
return EffectiveHolder.INSTANCE;
}
}
四:双重锁(不推荐)
public class JustOne {
//双重锁+禁止指令重排序
//volatile 禁止指令重排序,因为在jvm中创建一个对象,大致会有三个步骤
//1:给instance分配内存
//2:调用构造函数初始化成员变量
//3:将对象指向分配的空间
//由于jvm在创建对象的时候会进行指令优化,因此步骤可以是1-2-3,也可以是
//1-3-2。我们知道执行第三句指向分配的空间之后,instance就不是null了
//那么如果是第二种情况1-3-2,执行3之后,就被另一个线程抢占了,那么2还没有
//执行,也就是还没有初始化成员变量,因此当使用instance调用成员变量就会有问题
//jdk5之后volatile才有用。
private volatile static JustOne instance;
public static JustOne getInstance(){
if(instance==null){
synchronized(JustOne.class){
if(instance==null){
instance=new JustOne();
}
}
}
return instance;
}
}
五:枚举实现单例(推荐)
public enum Ali {
MAYUN;
private String anotherFiled;
Ali(){
anotherFiled="mayun";
}
public String getAnotherField(){
return anotherFiled;
}
}