对于单例模式就不做过多的介绍了,直接上代码
1.懒汉模式:延迟加载, 只有在真正使用的时候,才开始实例化。
public class LazySingleton {
private static volatile LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
我们一个对象的创建过程分为三步
- 申请内存空间,进行空间分配。
- 将对象进行初始化。
- 将instance 指向刚分配的内存地址。
了解了上面一个对象的创建过程后,下面来看这样一个场景:
假设上面的instance变量没有被volatile修饰,在多线程环境下,有可能会出现这样的情况,为了达到性能最大化,CPU和JIT可能会对我们的指令进行重排,就可能出现下面的步骤,第三步在第二步之前执行,也就是:
- 申请内存空间,进行空间分配。
- 将instance 指向刚分配的内存地址。
- 将对象进行初始化。
那么,当后面的线程进来的时候发现instance 不为null(这里注意,虽然对象没被初始化,但是并不是null)直接返回,当使用时因为对象并没被初始化,便会出现问题。volatile在这里的作用就是保证多线程环境下代码执行的有序性。
2.饿汉模式:类加载的初始化阶段就完成了实例的初始化。
class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
本质上就是借助于jvm类加载机制保证实例的唯一性及线程安全(JVM以同步的形式来完成类加载的整个过程)
3.静态内部类:本质上是利用类的加载机制来保证线程安全
class InnerClassSingleton {
private static class InnerClassHolder {
private static InnerClassSingleton instance = new InnerClassSingleton();
}
private InnerClassSingleton() {
if (InnerClassHolder.instance != null) {
throw new RuntimeException("Multiple instances are not allowed to be created");
}
}
public static InnerClassSingleton getInstance() {
return InnerClassHolder.instance;
}
}
这里说一下构造器里面的判断是为了防止别人用反射的方式去获取实例,出现多实例的情况。
4…枚举类型
public enum EnumSingleton {
INSTANCE;
public void print() {
System.out.println(this.hashCode());
}
}
不支持反射创建对应的实例,且有自己的反序列化机制。