单例模式的几种写法
1、懒汉式,非线程安全
private修饰构造方法使得从外部无法直接创建实例(反射除外),但是没有考虑多线程环境。
public class Singleton {
//懒汉式-非线程安全
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance () {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2、懒汉式,线程安全
加了一个synchronized就可以实现线程安全版本,但是效率太低
public class Singleton {
//懒汉式-线程安全
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance () {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
}
3、饿汉式
利用类加载机制在加载类时就创建了实例,但是没有达到lazy-loading的效果。
public class Singleton {
//饿汉式
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance () {
return instance
}
}
变种
public class Singleton {
//饿汉式
private static Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance () {
return instance;
}
}
4、静态内部类
静态内部类不会在外部类加载时加载,而是在第一次被调用时加载,所以这种方法也是可以达到延迟加载的。
public class Singleton {
//饿汉式-静态内部类
private static Singleton instance;
private static class Holder {
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance () {
return Holder.instance;
}
}
5、枚举
枚举还不是很理解。
public enum Singleton{
INSTANCE;
}
6、双重检查锁
还有一个方法就是大名鼎鼎的双重检查锁,可以同时达到线程安全和延迟加载两个要求。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
开始我不太明白为什么要加volatile关键字修饰instance,后面看到这篇文章:
原因是创建对象的过程也不是原子性的,
instance = new Singleton();
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址
//注意,此时对象还没有被初始化!
ctorInstance(memory); //2:初始化对象
所以仍有可能被编译器重排序导致多线程环境下创建出多个实例来,而加了volatile之后可以禁止编译器重排序,从而使得创建对象的过程可以在正确的顺序下完成。