什么是单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现方式
1.将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
2.在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
实现方式有以下几种:
饿汉式
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
/**
* 饿汉式单例
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
// 指向自己实例的私有静态引用,主动创建
private static Singleton INSTANCE = new Singleton();
// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton getInstance() {
return INSTANCE;
}
}
懒汉式
单例实例被延迟加载,即只有在真正使用的时候才会实例化一个对象并交给自己的引用。但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
public class Singleton {
// 私有的构造方法
private Singleton() {}
// 自己实例的私有静态引用
private static Singleton INSTANCE;
public static Singleton getInstance() {
// 被动创建,在真正需要使用时才去创建
if (null == INSTANCE) {
INSTANCE = new Singleton();
}
return INSTANCE;
}
}
双重加锁机制
public class Singleton {
// 私有构造方法
private Singleton() {}
// 此处使用valotile起到禁止指令重排的作用,可以保证双端检锁机制线程安全
private volatile static Singleton INSTANCE;
public static Singleton getInstance() {
if (null == INSTANCE) {
synchronized (Singleton.class) {
if (null == INSTANCE) {
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
这里voliate关键字的作用主要是禁止指令重排。初始化一个实例(INSTANCE = new Singleton())在java字节码中会有4个步骤:
- 申请内存空间,
- 初始化默认值(区别于构造器方法的初始化),
- 执行构造器方法
- 连接引用和实例。
这4个步骤后两个有可能会重排序,1234 1243都有可能,造成未初始化完全的对象发布。假设有个线程A发生了上面说的情况,执行到 INSTANCE = new Singleton(); 这一步,线程B执行判断null != INSTANCE,直接return未执行构造方法的对象返回。
详细分析双重检查锁定与延迟初始化
静态内部类
外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。即当Singleton 第一次被加载时,并不需要去加载SingletonHolder ,只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingletonHolder 类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
/**
* 静态内部类
*/
public class Singleton {
// 私有构造方法
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
public enum Singleton {
INSTANCE;
public void method() {
}
}








网友评论