单例模式
饿汉式
类加载时就创建唯一实例
优点:简单快捷,由jvm保证实例是单一实例
缺点:不管是否需要都会在类加载时创建实例
public class Test1{
//static 通过jvm保证实例唯一
private static final Test1 INSTANCE = new Test1();
//防止外部实例化
private Test1(){}
//外部调用方式
public static Test1 getInstance() {return INSTANCE;}
}
懒汉式
调用时才会创建实例
优点:访问时才会创建实例,不浪费空间
缺点:写法复杂,同时需要考虑线程安全
public class Test1{
//static 通过jvm保证实例唯一
private static final Test1 INSTANCE = null;
//防止外部实例化
private Test1(){}
//外部调用方式
public static Test1 getInstance() {
if(INSTANCE == null){
INSTANCE = new Test1();
}
return INSTANCE;
}
}
在多线程并发进入时,会造成多次赋值
双重检查
public class Test1{
//static 通过jvm保证实例唯一
private static volatile Test1 INSTANCE = null;
//防止外部实例化
private Test1(){}
//外部调用方式 锁住对象(颗粒更小,耗时更小)
public static Test1 getInstance1() {
if(INSTANCE == null){
synchronize(Test1.class){
if(INSTANCE == null){
INSTANCE = new Test1();
}
}
}
return INSTANCE;
}
}
这种能保证线程安全
加volatile的原因是为了保证指令不重排,因为赋值在指令级别有三条指令,如果先指向对象后给对象赋值,这种情况下会导致第二次进来的线程拿到的是一个不正确的对象。
静态内部类
静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
public class Test1{
private static class Test2{
private static final Test1 INSTANCE = new Test1();
}
private Test1(){}
public static final Test1 getInstance(){
return Test2.INSTANCE;
}
}
枚举
枚举的方式是比较少见的一种实现方式,但是看上面的代码实现,却更简洁清晰。并且她还自动支持序列化机制,绝对防止多次实例化。
public enum Test{
INSTANCE;
public void value(){}
}