单例模式

懒汉式和饿汉式区别:
实例化方面: 懒汉式默认不会实例化,外部什么时候调用什么时候new。饿汉式在类加载的时候就实例化,并且创建单例对象。
线程安全方面:
饿汉式线程安全:在线程还没出现之前就已经实例化了,因此饿汉式线程一定是安全的)。
懒汉式线程不安全:因为懒汉式加载是在使用时才会去new实例的

饿汉式

一上来直接创建对象

类加载的时候就实例化,并且创建单例对象。

public class Hungry {
    private Hungry() {

    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}

懒汉式

默认不会实例化,什么时候用什么时候new。

public class Lazy {
    private Lazy() {

    }

    private static Lazy lazy;

    public static Lazy getInstance() {
        if (lazy == null) {
            lazy = new Lazy();
        }
        return lazy;
    }
}

单线程情况下可以,那多线程呢

public class Lazy {
    private Lazy() {
        System.out.println(Thread.currentThread().getName() + "ok");

    }

    private static Lazy lazy;

    public static Lazy getInstance() {
        if (lazy == null) {
            lazy = new Lazy();
        }
        return lazy;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lazy.getInstance();
            }).start();
        }
    }
}

image-20220925084928223

image-20220925084934137

运行结果每次都不一样

进阶

双重检测锁模式(DCL懒汉式)

public class Lazy {
    private Lazy() {
        System.out.println(Thread.currentThread().getName() + "ok");

    }

    private static Lazy lazy;

    //双重检测锁模式(DCL懒汉式)
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();
                }
            }
        }

        return lazy;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lazy.getInstance();
            }).start();
        }
    }
}

在极端情况下有问题

image-20220925085229779

new 不是原子性操作

  1. 分配内存空间
  2. 执行构造方法,初始化对象
  3. 把这个对象指向这个空间

举个例子

期望123

真实可能是132

A线程 进来132(先占空间再放内存对象)

突然来了B线程,线程B指向了这个空间,lazy就!=null,直接return 了lazy,lazy其实还没有完成构造,空间是一片虚无

由于指令重排可能导致的现象

加volatile

完整的双重检测锁加原子性操作

public class Lazy {
    private Lazy() {
        System.out.println(Thread.currentThread().getName() + "ok");

    }

    private volatile static Lazy lazy;

    //双重检测锁模式(DCL懒汉式)
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();
                }
            }
        }

        return lazy;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                Lazy.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 Lazy {
    private Lazy() {
        System.out.println(Thread.currentThread().getName() + "ok");

    }

    private volatile static Lazy lazy;

    //双重检测锁模式(DCL懒汉式)
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();
                }
            }
        }

        return lazy;
    }

    public static void main(String[] args) throws Exception {
        Lazy lazy1 = Lazy.getInstance();
        //获得空参构造器
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        //暴力
        declaredConstructor.setAccessible(true);
        Lazy lazy2 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);
    }
}

hash值不同,已经不是一个对象了

image-20220925090911684

在构造器中加锁

   private Lazy() {
        synchronized (Lazy.class) {
            if (lazy != null) {
                throw new RuntimeException("不要使用反射破坏");
            }
        }
    }

完整代码

public class Lazy {
    private Lazy() {
        synchronized (Lazy.class) {
            if (lazy != null) {
                throw new RuntimeException("不要使用反射破坏");
            }
        }

    }

    private volatile static Lazy lazy;

    //双重检测锁模式(DCL懒汉式)
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();
                }
            }
        }

        return lazy;
    }

    public static void main(String[] args) throws Exception {
        //Lazy lazy1 = Lazy.getInstance();
        //获得空参构造器
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        //暴力
        declaredConstructor.setAccessible(true);
        Lazy lazy2 = declaredConstructor.newInstance();
        Lazy lazy1 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);
    }
}

那2个对象都是反射拿到构造器然后创建呢

Lazy lazy2 = declaredConstructor.newInstance();
Lazy lazy1 = declaredConstructor.newInstance();
System.out.println(lazy1);
System.out.println(lazy2);

单例模式又被破坏了

image-20220925092551045

设置一个标志位,走构造器就会改变

  private static boolean aka = false;
    private Lazy() {
        synchronized (Lazy.class) {
            if (aka == false) {
                aka = true;
            }else {
                throw new RuntimeException("不要使用反射破坏");
            }
        }
    }

完整代码

public class Lazy {
    private static boolean aka = false;
    private Lazy() {
        synchronized (Lazy.class) {
            if (aka == false) {
                aka = true;
            }else {
                throw new RuntimeException("不要使用反射破坏");
            }
        }
    }

    private volatile static Lazy lazy;

    //双重检测锁模式(DCL懒汉式)
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();
                }
            }
        }

        return lazy;
    }

    public static void main(String[] args) throws Exception {
        //Lazy lazy1 = Lazy.getInstance();
        //获得空参构造器
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        //暴力
        declaredConstructor.setAccessible(true);
        Lazy lazy2 = declaredConstructor.newInstance();
        Lazy lazy1 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);
    }
}

image-20220925092812028

不通过反编译的话是拿不到这个标志的

如果我们能知道这个标志位,我们就可以绕过

Field aka = Lazy.class.getDeclaredField("aka");
        aka.setAccessible(true);

        //获得空参构造器
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        //暴力
        declaredConstructor.setAccessible(true);
        Lazy lazy2 = declaredConstructor.newInstance();
        aka.set(lazy2,false);
        Lazy lazy1 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);

完整代码

public class Lazy {
    private static boolean aka = false;
    private Lazy() {
        synchronized (Lazy.class) {
            if (aka == false) {
                aka = true;
            }else {
                throw new RuntimeException("不要使用反射破坏");
            }
        }
    }

    private volatile static Lazy lazy;

    //双重检测锁模式(DCL懒汉式)
    public static Lazy getInstance() {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy();
                }
            }
        }

        return lazy;
    }

    public static void main(String[] args) throws Exception {
        //Lazy lazy1 = Lazy.getInstance();
        Field aka = Lazy.class.getDeclaredField("aka");
        aka.setAccessible(true);

        //获得空参构造器
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor();
        //暴力
        declaredConstructor.setAccessible(true);
        Lazy lazy2 = declaredConstructor.newInstance();
        aka.set(lazy2,false);
        Lazy lazy1 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);
    }
}

image-20220925093137422

看看源码

image-20220925093530006

枚举是jdk1.5就有的

枚举本身就是一个Class类

public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }


}

class Test {
    public static void main(String[] args) {
        EnumSingle instance = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        System.out.println(instance);
        System.out.println(instance2);
    }
}

进target里看发现有无参构造器

image-20220925094146714

反射破解一波

public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }


}

class Test {
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
      /*  EnumSingle instance2 = EnumSingle.INSTANCE;*/
        System.out.println(instance==instance2);
    }
}

没想到idea骗人

image-20220925094216575

枚举类没有空参构造器

分析一下源码

反编译

image-20220925100522990

还是有构造器,用jad试试

jad官网点这里去

image-20220925101159652

有参构造器

image-20220925101245827

反射修改一下

image-20220925101336362

抛出了想要的异常

image-20220925101349524

Q.E.D.


春风亦有春风愁,不劳春风为我忧