单例模式是我们比较常用的设计模式之一。一般有这几种比较常用的写法:
1、饿汉模式
public class HungrySingleton {
private static HungrySingleton mInstance = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return mInstance;
}
}
这种模式基于classloader机制,避免了多线程同步的问题,但是由于在类加载就实例化可能会造成内存的浪费。我们比较喜欢用到的时候再实例化,也就是我们常说的lazy loading效果。
2、懒汉模式
public class LazySingleton {
private static LazySingleton mInstance;
private LazySingleton(){}
public static LazySingleton getInstance(){
if (mInstance==null){
mInstance = new LazySingleton();
}
return mInstance;
}
}
懒汉模式很明显使用的是lazy loading。但是它有一个致命的弱点就是没办法在多线程中正常工作。改进方式是给getInstance()添加synchronized关键字,这样一来既保留了lazy loading,也能够在多线程中工作。但是会导致新的问题:效率过低,因为大多数情况都不需要同步。
3、静态内部类模式
public class InnerSingleton {
private InnerSingleton (){}
public static InnerSingleton getInstance(){
return Inner.INSTANCE;
}
private static class Inner{
private static final InnerSingleton INSTANCE = new InnerSingleton();
}
}
这种模式的单例,可以正常的在多线程中工作,同时也达到了lazy loading效果。推荐使用
4、双重校验加锁单例
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton mInstance;
private DoubleCheckSingleton (){}
public static DoubleCheckSingleton getInstance(){
if (mInstance==null){
synchronized (DoubleCheckSingleton.class){
if (mInstance==null){
mInstance = new DoubleCheckSingleton();
}
}
}
return mInstance;
}
}
首先很明显能够看到getInstance()方法中有两次判空操作,volatile和synchronized关键字能够有效的保证了再多线程中的正确运行。但是由于volatile关键字是在jdk1.5才有的。这种方式在1.5之前可能会导致多线程运行异常。也就是常说的DCL失效。
5、枚举单例
public enum EnumSingleton {
INSTANCE;
public void doSomething(){
// TODO
System.out.println("我是枚举单例");
}
}
写法简单是枚举单例的优点。因为枚举和普通的java类一样,可以拥有变量和方法。并且默认枚举是多线程安全,因此这也是一种比较推荐的写法。