在使用java编码的过程中经常会去写单例模式,今天总结一下6种常见单例写法.
饿汉模式
public class HungerSingleton {
private static HungerSingleton singleton = new HungerSingleton();
private HungerSingleton() {
}
public static HungerSingleton getHungerSingletonInstance(){
return singleton;
}
}
评价:这种模式直接在类初始化的时候实例化单例,线程安全,但是没有延迟加载。
懒汉模式
public class LazySingleton {
private static LazySingleton singleton;
private LazySingleton() {
}
public static LazySingleton getLazySingletonInstance(){
if (singleton == null){
singleton = new LazySingleton();
}
return singleton;
}
}
评价:这种模式在需要使用该对象的时候时候实例化单例,做到了延迟加载,节约内存,但是非线程安全。
懒汉线程安全模式
public class LazyThreadSafeSingleton {
private static LazyThreadSafeSingleton singleton;
private LazyThreadSafeSingleton() {
}
public synchronized static LazyThreadSafeSingleton getLazyThreadSafeSingletonInstance(){
if (singleton == null){
singleton = new LazyThreadSafeSingleton();
}
return singleton;
}
//或者
public static LazyThreadSafeSingleton getLazyThreadSafeSingletonInstance(){
synchronized (LazyThreadSafeSingleton.class){
if (singleton == null){
singleton = new LazyThreadSafeSingleton();
}
return singleton;
}
}
}
评价:同时做到了线程安全,而且延迟加载,但是每次getLazyThreadSafeSingletonInstance都会使用synchronized关键字影响了效率.
DCL模式模式
public class DclSingleton {
private static volatile DclSingleton mInstance = null;
public static DclSingleton getInstance() {
if (mInstance == null){
synchronized (DclSingleton.class){
if (mInstance == null){
mInstance = new DclSingleton();
}
}
}
return mInstance;
}
}
评价:这里我们进行了双重check,第一次check没有synchronized关键字,比懒汉线程安全节约了效率,第二次check的时候加入了synchronized关键字再一次进行了一次是否为null的判断,这样保证在多线程并且mInstance都为null情况下进行重复创建。注意mInstance = new DclSingleton()这句代码,它实际上并不是一个原子操作,这条代码最终会编译成多条汇编指令,它大概会做三件事
- 给DclSingleton分配内存
- 调用DclSingleton构造函数,初始化成员字段
- 将mInstance对象指向分配的内存控件(此时mInstance就不是null了)
上面2,3顺序是不能保证顺序执行的,也就是说顺序可能是123也有可能是132,也就是说在3执行完毕,2未执行完的情况,会存在其它线程调用getInstance()直接返回mInstance的情况。所以我们必须加上volatile关键字,保证不会出现132这种情况
静态内部类模式
public class StaticInnerSingleton {
private StaticInnerSingleton(){
}
public static StaticInnerSingleton getInstance(){
return StaticInnerHolder.sInstance;
}
public static class StaticInnerHolder{
public static final StaticInnerSingleton sInstance = new StaticInnerSingleton();
}
}
评价:这种单例模式简单,线程安全,并且延迟加载,推荐使用
枚举模式
public enum EnumSingleton {
INSTANCE;
public void doSomeThing(){
System.out.println("do sth");
}
}
评价:这种单例模式简单,线程安全
总结:任何单例模式都是将构造函数私有化,对外提供统一获取唯一实例的接口,在选择哪种模式要考虑线程安全,延迟加载因素。
网友评论