深入单例模式

PPG007 ... 2021-12-28 About 1 min

# 深入单例模式

# 饿汉式

public class Hungry {

//    饿汉式,开始时就分配全部空间,可能浪费空间
    byte[][] bytes = new byte[1024][1024];


    private Hungry(){

    }
    private static final Hungry HUNGRY =new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 懒汉式

普通懒汉式未实现线程安全:

public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+" run");
    }

    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();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

双重检测的懒汉式(DCL):

存在指令重排可能的代码:

public class LazyMan {

    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+" run");
    }

    private static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if (lazyMan==null){
                    lazyMan=new LazyMan();//非原子性操作
                    // 1.分配内存空间
                    // 2.执行构造方法
                    // 3.把结果赋值给这个引用
                    // 期望执行顺序:1 2 3
                    // 现在有一个线程A,由于指令重排,导致1 3 2
                    // 此时有一个线程B,在A执行完1 3后进入,由于引用已被赋值
                    // 线程B会直接返回还没有被创建的对象引用
                }
            }
        }

        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

使用volatile消除指令重排:

private volatile static LazyMan lazyMan;
1

静态内部类:

public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.holder;
    }
    public static class InnerClass{
        private static Holder holder=new Holder();
    }
}
1
2
3
4
5
6
7
8
9
10
11

枚举类可以禁止使用反射创建实例:

public enum EnumDemo {

    INSTANCE;

    private EnumDemo getInstance(){
        return INSTANCE;
    }

}
class Test{
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        EnumDemo instance = EnumDemo.INSTANCE;
        System.out.println(instance.hashCode());
        Class<EnumDemo> enumDemoClass = EnumDemo.class;
        // 通过jad反编译获取真实的构造器
        Constructor<EnumDemo> declaredConstructor = enumDemoClass.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        // Cannot reflectively create enum objects
        EnumDemo enumDemo = declaredConstructor.newInstance();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Last update: December 28, 2021 13:51
Contributors: PPG007