单例模式的实现方式
名称 | 说明 |
---|---|
懒汉式 | 线程不安全 |
懒汉式 线程安全 | 线程安全,开销大 |
饿汉式 | 线程安全,缺点是类加载的时候就初始化了 |
双重检验锁 | 线程安全,使用时才实例化,缺点是太复杂了 |
静态内部类 | 线程安全,使用时才实例化,实现起来比上面要简单一点,最常用的单例实现方式 |
枚举 | 利用枚举本身的特性实现单例,最推荐的做法 |
1 懒汉式
public class Evis {
private static Evis instance;
private Evis() {}
public static Evis getInstance() {
if (instance == null){
instance = new Evis();
}
return instance;
}
}
2 懒汉式 线程安全
public class Evis {
private static Evis instance;
private Evis() {}
public static synchronized Evis getInstance() {
if (instance == null){
instance = new Evis();
}
return instance;
}
}
3 饿汉式
public class Evis {
private static final Evis INSTANCE = new Evis();
private Evis() {}
public static Evis getInstance() {
return INSTANCE;
}
/**
* 防止反序列化时,多次实例化该变量
*/
private Object readResolve(){
return INSTANCE;
}
}
4 双重检验锁
public class Evis {
private volatile static Evis instance;
private Evis() {}
public static Evis getInstance() {
if (instance == null) {
synchronized (Evis.class) {
if (instance == null) {
instance = new Evis();
}
}
}
return instance;
}
}
instance = new Evis()
这句代码不是原子性的,在JVM内部大概分三步进行的:
1. 给 instance 分配内存
2. 调用 Evis 的构造函数来初始化成员变量
3. 将instance对象指向分配的内存空间(执行完这步instance就为非null了)
因为JVM内部有指令重排序优化,这三步的执行顺序是不能保证的,有可能是1->2->3,也有可能是1->3->2。当是后者时,并且执行到3,恰好有另外一个线程访问该方法,就有可能返回一个未调用构造函数初始化,但不为null的变量。因此需要在声明前加volatile
,禁止重排序优化。
5 静态内部类
public class Evis {
private static class SingletonHolder {
private static final Evis INSTANCE = new Evis();
}
private Evis() {}
public static Evis getInstance() {
return SingletonHolder.INSTANCE;
}
}
6 枚举
public enum Evis {
INSTANCE;
public void leaveTheBuilding(){...}
}
类加载器
如果单例类由多个类加载器装载,有可能存在多个实例,以下方法可以防止这个问题
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader ==Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Evis.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
欢迎大家访问我的博客,转载请注明出处
http://blog.youkuaiyun.com/abyss521