饿汉式
// 饿汉式单例 public class Hungry { //构造器私有 private Hungry(){ } // 一上来就把这个类加载了 private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance(){ return HUNGRY; } } // 饿汉式单例 public class Hungry { // 这4组数据非常耗内存资源,饿汉式一上来就把所有的内存里面的东西全部加载进来了,就存在这个空间 // 但这个空间现在是没有使用的,可能会造成浪费空间 private byte[] data1 = new byte[1024*1024]; private byte[] data2 = new byte[1024*1024]; private byte[] data3 = new byte[1024*1024]; private byte[] data4 = new byte[1024*1024]; //构造器私有 private Hungry(){ } // 一上来就把这个类加载了 private final static Hungry HUNGRY = new Hungry(); public static Hungry getInstance(){ return HUNGRY; } }饿汉式单例可能会造成浪费空间,所以想要用的时候再去创建这个对象,平时就先放在这个地方,于是就出现了懒汉式!
懒汉式
// 懒汉式单例 public class LazyMan { // 构造器私有 private LazyMan(){ } private static LazyMan lazyMan; public static LazyMan getInstance(){ if (lazyMan==null){ lazyMan = new LazyMan(); } return lazyMan; } }它是有问题的,单线程下确实单例ok,多线程并发就会出现问题!
测试
// 懒汉式单例 public class LazyMan { // 构造器私有 private LazyMan(){ System.out.println(Thread.currentThread().getName()+":: ok"); } private static LazyMan lazyMan; public static LazyMan getInstance(){ if (lazyMan==null){ lazyMan = new LazyMan(); } return lazyMan; } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(() -> { LazyMan.getInstance(); }).start(); } } }发现单例有问题,每次结果可能都不一样!
解决
// 懒汉式单例 public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+":: ok"); } private static LazyMan lazyMan; // 双重检测锁模式的 懒汉式单例 DCL懒汉式 public static LazyMan getInstance(){ if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) { for (int i = 0; i < 10 ; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }但在极端情况下还是可能出现问题
经历三个步骤:
1、 分配内存空间
2、 执行构造方法,初始化对象
3、 把这个对象指向这个空间
有可能会发生指令重排的操作!
比如,期望它执行 123 ,但是它真实可能执行132,比如第一个A线程过来执行了132,先分配空间再吧这个空间占用了,占用之后再去执行构造方法,如果现在突然来了个B线程,由于A已经指向这个空间了,它会以为这个 lazyMan 不等于 null ,直接return ,此时lazyMan还没有完成构造,所有必须避免这个问题!
必须加上volatile
// 懒汉式单例 public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+":: ok"); } // 避免指令重排 private volatile static LazyMan lazyMan; // 双重检测锁模式的 懒汉式单例 DCL懒汉式 public static LazyMan getInstance(){ if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); } } } return lazyMan; } public static void main(String[] args) { for (int i = 0; i < 10 ; i++) { new Thread(()->{ LazyMan.getInstance(); }).start(); } } }静态内部类
// 静态内部类 public class Holder { private Holder(){ } public static Holder getInstance(){ return InnerClass.HOLDER; } public static class InnerClass{ private static final Holder HOLDER = new Holder(); } }也是单例模式的一种,不安全!
单例不安全 反射
// 懒汉式单例 public class LazyMan { private LazyMan(){ System.out.println(Thread.currentThread().getName()+":: ok"); } private volatile static LazyMan lazyMan; // 双重检测锁模式的 懒汉式单例 DCL懒汉式 public static LazyMan getInstance(){ if (lazyMan==null){ synchronized (LazyMan.class){ if (lazyMan==null){ lazyMan = new LazyMan(); //不是一个原子性操作 } } } return lazyMan; } //反射 public static void main(String[] args) throws Exception { LazyMan instance1 = LazyMan.getInstance(); Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); // 无视了私有的构造器 // 通过反射创建对象 LazyMan instance2 = declaredConstructor.newInstance(); System.out.println(instance1); System.out.println(instance2); } }