/**
* 懒汉式
* 就是等到有线程调用getInstance这个方法时,才来创建对象实例
*/
public class Singleton01 {
private Singleton01 instance = null;
private Singleton01(){}
public Singleton01 getInstance(){
if (instance == null){
instance = new Singleton01();
}
return instance;
}
}
/**
* 双重检测机制
* 当instance= =null时,假如有两个线程p1,p2进入了第一个if语句,之后p1进入的同步块中,成功创建了对象实例,
* 这时候论到p2进入同步块,由于同步块还有一层if(instance= =null)的判断,
* 又因为此时instance != null了,所以p2无法再创建新的实例对象。
*
* 通过volatile来保证指令重排问题
* instance = new Singleton
* 这段代码在虚拟机中是这样执行的:
*
* memory = allocate();//1: 给对象分配内存空间。
*
* ctorInstance(memory);//2: 初始化对象
*
* instance = memory; //3: 把instance变量指向刚刚分配的内存地址。
*
* 问题的根源就是指令重排的影响,可能执行顺序改变导致创建对象出现问题,
* 所以我们只要保证在创建对象的时候,不要出现指令重排就可以了。
* 只有把instance声明为volatile,那么虚拟机就会保证这三个动作按照顺序执行了,也就不会出现线程安全问题了。
*/
public class Singleton02 {
private volatile Singleton02 instance = null;
private Singleton02(){}
public Singleton02 getInstance(){
if (instance == null){
synchronized (Singleton02.class){
if (instance == null){
instance = new Singleton02();
}
}
}
return instance;
}
}
/**
* 饿汉式
* 就是一开始把对象实例创建出来,而不是等getInstance这个方法被调用才来创建对象
*
* 总结一下饿汉式的一些问题:
*
* 1、有可能出现对象白白浪费的情况。
*
* 2、和懒汉式一样,无法阻止反射问题。
*/
public class Singleton03 {
private Singleton03 instance = new Singleton03();
private Singleton03(){}
public Singleton03 getInstance(){
return instance;
}
}
/**
* 由于外部类无法访问静态内部类,因此只有当外部类调用Singleton.getInstance()方法的时候,才能得到instance实例。
*
* 并且,instance实例对象初始化的时机并不是在Singleton被加载的时候,而是当getInstance()方法被调用的时候,静态内部类才会被加载,这时instance对象才会被初始化。并且也是线程安全的。
*
* 所以,与饿汉式相比,通过静态内部类的方式,可以保证instance实例对象不会被白白浪费。
*
* 但是,它仍然存在反射问题。
*/
public class Singleton04 {
private static class instanceHolder{
private static Singleton04 instance = new Singleton04();
}
private Singleton04(){}
public Singleton04 getInstance(){
return instanceHolder.instance;
}
}
/**
* 和饿汉式一样,由于一开始instance实例就被创建了,所以有可能出现白白浪费的情况。
*
* 但是,通过枚举的方式,不仅代码简单,线程安全,而且JVM还能阻止反射获取枚举类的私有构造器。
*
* 线程安全:Java确保每个枚举常量只被实例化一次,在类加载时就完成实例化,这天然保证了线程安全。
* 简单易用:使用枚举实现单例模式代码简洁,易于理解和实现。
* 防止反射攻击:由于枚举类没有公开的构造器,因此无法通过反射来创建枚举类型的多个实例。
* 防止序列化问题:枚举实例的序列化和反序列化不会创建新的对象,这保护了单例的状态。
* 自动支持序列化机制:不需要显式实现序列化接口,枚举类型已经自动支持序列化和反序列化。
*/
public enum Singleton05 {
instance;
}