单例模式(Singleton Pattern)作为Java设计模式之一,保证一个类仅有一个实例,并提供一个访问它的全局访问点。节省系统资源,也适用于需要全局实例的场景。本文中介绍的四种单例模式皆为线程安全的写法。
1. 双重检查
public class MySingletonDoubleCheck {
private static volatile MySingletonDoubleCheck singleton;
private MySingletonDoubleCheck(){
}
private static MySingletonDoubleCheck getInstence(){
if (singleton == null){
synchronized (MySingletonDoubleCheck.class){
if (singleton == null){
singleton = new MySingletonDoubleCheck();
return singleton;
}
}
}
return singleton;
}
}
主要思想
定义了私有的静态成员变量,volatile关键字可以禁止指令重排序,同时写了一个私有的空的无参构造方法防止强行使用new实例出一个变量。synchronized (MySingletonDoubleCheck.class)
保证了当同时有两个线程想要实例化singleton变量时,只会让第一个线程实例化出变量,等到第二个线程进入同步代码块时singleton变量已经不为null了。
volatile关键字的作用
volatile关键字的作用:在new出一个新对象的过程中,计算机其实将其分成了3个步骤,第一步首先给singleton分配内存,第二步随后调用构造方法初始化,第三步最后将singleton对象指向分配的内存空间,此时singleton才不为null。看起来顺理成章,但是计算机为了提高效率,在不影响结果的情况下,可能会改变这三个步骤的顺序,也就是说,可能会先执行第三步,再执行第二步。注意,执行完第三步之后,singleton变量就已经不是null了,但实际上它还没有完成初始化,此时如果有第二个线程也想要得到单例对象,在第一次判断singleton是否为null时就将尚未初始化的singleton返回,此时返回的singleton是不完整的。
优点
延迟加载,效率高。
缺点
代码稍稍复杂了一点,需要对锁有一定理解,同时volatile也会影响一点点性能,但无伤大雅。
2. 饿汉式(静态常量)
public class Singleton {
private static final Singleton INSTANCE = new SingletonSample();
private Singleton(){
}
public static Singleton getInstance(){
return INSTANCE;
}
}
主要思想
采用静态常量的方式实现单例,十分简单,但依旧不能忘了写一个私有无参构造方法。
优点
写法简单。
缺点
在类装载时就被实例化,没有达到懒加载(Lazy Loadind)的效果,有可能造成资源浪费,比如从始至终都未使用过这个实例。
3. 静态内部类
public class SingletonStatic {
private SingletonStatic(){
}
private static class SingletonInstance{
private static final SingletonStatic SINGLETON_STATIC = new SingletonStatic();
}
public static SingletonStatic getInstance(){
return SingletonInstance.SINGLETON_STATIC;
}
}
主要思想
采用静态内部类的形式,思想与饿汉式类似,但静态内部类在类加载时不会被实例化,只有在第一次调用getInstance()方法时才会加载,类的静态属性只会在第一次加载类的时候初始化,所以JVM保证了线程的安全性,在类进行初始化时,别的线程无法干扰。
优点
延迟加载,效率高。
缺点
没有什么缺点。
4. 枚举+接口
- 以下为自定义接口:
public interface MySingleton {
void doSomething();
}
- 利用枚举实现单例模式:
public enum Singleton implements MySingleton {
INSTANCE {
@Override
public void doSomething() {
System.out.println("It is my singleton");
}
};
public static Singleton getInstance(){
return Singleton.INSTANCE;
}
}
主要思想
借助JDK1.5中添加的枚举来实现单例模式。
优点
不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
缺点
代码较其他三种比较怪异。