6种单例模式写法了解一下
目录
饿汉式 (线程都是安全滴)
简单饿汉式(直接实例化)
public class HungerSimpleSingleton {
public static final HungerSimpleSingleton instance = new HungerSimpleSingleton();
private HungerSimpleSingleton(){}
}
静态代码块(因为在类加载的时候只执行一次)
public class HungerStaticSingleton {
public static final SingletonObj INSTANCE;
static {
//假如这里还需要加载外部资源等复杂逻辑
INSTANCE = new SingletonObj();
}
private HungerStaticSingleton(){}
}
枚举单例(反编译可以看出端倪)
public enum EnumSingleton {
INSTANCE;
}
反编译后的结果,见文章
Java的Enum枚举反编译的结果
懒汉式 (线程有安全也有不安全)
简单懒汉式(线程不安全)
public class LazySimpleSingleton {
private static LazySimpleSingleton instance;
private LazySimpleSingleton(){}
private static LazySimpleSingleton getInstance(){
//这里也是产生了竞态条件(Race Condition),导致了线程不安全)
if (instance == null){
instance = new LazySimpleSingleton();
}
return instance;
}
}
同步懒汉式(使用了volatile以及synchronized,双重判断)
第一种 同步方法
public class LazySyncSingleton {
private static LazySyncSingleton instance;
private LazySyncSingleton(){}
//这里使用了 synchronized
public synchronized static LazySyncSingleton getInstance(){
if (instance == null) {
try {
//为了保证效果,这里睡2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazySyncSingleton();
}
return instance;
}
}
第二种 同步代码块+volatile+double-check
这一种有些麻烦,要考虑的问题很多,一是效率问题,再同步代码块外部增加了
if
判断,二是指令重排
问题,虽然我们理想的new对象应该是①申请内存空间②实例化对象③内存地址指向变量,但是由于②③没有数据依赖
在编译过程中,虚拟机可能会帮我们“优化”
顺序,变为③②,即造成了先指向了变量,但是实例化未完成。当然,单线程环境会保证最终一致性。
public class LazyVolatileSingleton {
//防止指令重排
private static volatile LazyVolatileSingleton instance;
private LazyVolatileSingleton(){}
public static LazyVolatileSingleton getInstance(){
//这里要注意,如果没有数据依赖的话,多线程环境可能会出现指令重排,执行的顺序不会按照代码的顺序执行
//如果这里是有两条线程,a线程正在初始化,这个时候b现在开始判断,instance!=null,但是地址指向的是null。
//因为在new对象的过程中是先申请内存空间,再实例化对象,如果这里刚申请完空间给instance,那么它不为空,但是其他线程根据
//这个地址去找的时候,优化a线程new操作还未完成,所以这里有地址指向,但是还是“空”的,所以这也是造成了线程安全问题。
//加快效率,如果顺利的判断不等于null了可以不用获取锁直接返回
if (instance == null) { //TODO 这一行要注意
//使用内置锁,保证不被其他线程干扰,保证操作原子性
synchronized (LazyVolatileSingleton.class) {
if (instance == null) {
try {
//为了保证效果,这里睡2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
instance = new LazyVolatileSingleton();//TODO 这一行要注意
}
}
}
return instance;
}
}
静态内部类懒汉式
/**
* 在内部类被加载初始化的时候,才会创建父类的INSTANCE对象
*/
public class LazyInnerSingleton {
private LazyInnerSingleton(){}
private static class Inner{
private static LazyInnerSingleton INSTANCE = new LazyInnerSingleton();
}
public static LazyInnerSingleton getInstance(){
return Inner.INSTANCE;
}
}