1.饿汉式
public class DesignMode {
//构造器私有
private DesignMode(){
}
//饿汉式单例模式
private static final DesignMode designMode=new DesignMode();
public DesignMode getInstance (){
return designMode;
}
}
饿汉式类内部的对象会在类的初始化过程中就被创建,即使后续可能根本没用到我们的designMode对象,也会被创建出来,这样的缺点是可能会浪费空间
2.懒汉式
public class DesignMode {
//构造器私有
private DesignMode(){
}
//懒汉式单例模式
private static DesignMode designMode;
public static DesignMode getInstance (){
if(designMode==null){
designMode=new DesignMode();
}
return designMode;
}
}
当第一次被要求获得对象时才会被初始化,避免了空间浪费的问题。但是在多线程状态下,可能会出现并发问题,下面介绍解决方法
2.1双重检测锁(DCL)懒汉式
public class DesignMode {
//构造器私有
private DesignMode(){
}
//懒汉式单例模式
private static DesignMode designMode;
public static DesignMode getInstance (){
if(designMode==null){
synchronized (DesignMode.class){
if (designMode==null){
designMode=new DesignMode();
}
}
}
return designMode;
}
}
两次检查是否为空(两次检查的理由是:第一次是在没有同步的情况下,如果检查出不为空,那就省事了,直接用designMode就行,否则就同步了之后再检查一次是否为空)。
但是这个方法仍然不是完美的,由于new DesignMode这个过程不是原子性的,因此存在一种可能,即A线程尚未完成对象的初始化,但是designMode这个指针已经指向了一片地址空间(即不为null),与此同时B线程视图直接把designMode对象返回,这样就会出现问题。
解决方法是声明designMode为volatile的。
public class DesignMode {
//构造器私有
private DesignMode(){
}
//懒汉式单例模式
private static volatile DesignMode designMode;
public static DesignMode getInstance (){
if(designMode==null){
synchronized (DesignMode.class){
if (designMode==null){
designMode=new DesignMode();
}
}
}
return designMode;
}
}
3.反射单例
即使完成了上面的这些优化,懒汉式仍然存在一些缺陷。比如可以使用反射来躲过我们在getInstance中的检查,来直接调用私有的构造器方法。要解决这个问题可以使用枚举,java从底层源码的层面避免了枚举类型被反射破坏单一性的情况(无法用反射的newInstance方法来创建一个枚举类)。
public enum DesignMode {
Instance;
public static DesignMode getInstance(){
return Instance;
}
}