Jason's drunk words

Drunk more,talk more!

设计模式-创建型

单例模式

单例模式,顾名思义就是一个类只有一个实例。 单例主要的好处就是,1:可以解决资源访问冲突的问题。2:减少资源浪费。

单例的实现方式主要有以下几种。

1:饿汉式

在类加载的时候就实力化对象,不支持延迟加载。

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
public class HungryDemo {
    private AtomicInteger id = new AtomicInteger(0);
    public static HungryDemo instance = new HungryDemo();

    private HungryDemo() {
    }

    public static HungryDemo getInstance() {
        return instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                HungryDemo instance = HungryDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

2:懒汉式

支持延迟加载,在使用的时候才真正加载。

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
public class LazyDemo {
    private AtomicInteger id = new AtomicInteger(0);

    public static LazyDemo instance;

    private LazyDemo() {
    }

    public static synchronized LazyDemo getInstance() {
        if (Objects.isNull(instance)) {
            instance = new LazyDemo();
        }
        return instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                LazyDemo instance = LazyDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

饿汉和懒汉的区别就是一个支持延迟加载,一个不支持。懒汉模式还要加锁,防止并发问题。从这一点来看,懒汉模式性能是不如饿汉模式,饿汉模式在类加载时就进行实力化,也可以提前将实力化过程中发生的资源不足等问题提前暴露,而不是等到业务访问后才发现无法进行初始化,引发线上事故。 但这两种都有各自的不足,所以现在有第三种方式。

3:双重检测

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
36
37
public class DoubleCheckDemo {
    private AtomicInteger id = new AtomicInteger(0);
    public static DoubleCheckDemo instance;

    private DoubleCheckDemo() {
    }

    public static DoubleCheckDemo getInstance() {
        if (Objects.isNull(instance)) {
            synchronized (DoubleCheckDemo.class) {
                System.out.println("加锁操作");
                if (Objects.isNull(instance)) {
                    instance = new DoubleCheckDemo();
                }
            }
        }
        return instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                DoubleCheckDemo instance = DoubleCheckDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

双重检测解决了懒汉模式的并发性能问题,同时支持懒加载。

4:静态内部类

静态内部类的实现比双重检测要更加简单,同时也能做到懒加载。

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
public class InnerClassDemo {

    private AtomicInteger id = new AtomicInteger(0);

    private InnerClassDemo() {
    }

    private static class InnerClassDemoHolder {
        private static final InnerClassDemo instance = new InnerClassDemo();
    }

    public static InnerClassDemo getInstance() {
        return InnerClassDemoHolder.instance;
    }

    public int getId() {
        return id.incrementAndGet();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                InnerClassDemo instance = InnerClassDemo.getInstance();
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

InnerClassDemoHolder 是静态内部类,一开始并不会加载,等到调用getInstance()方法时才会被加载,这是一种无锁的实现,线程安全由jvm来保证。

5:枚举

采用枚举的方式来实现是最简单的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public enum EnumDemo {
    INSTANCE;
    private AtomicInteger id = new AtomicInteger(0);

    public int getId() {
        return id.incrementAndGet();
    }
}
public class EnumDemoTest {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(new Random().nextInt(10000));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                EnumDemo instance = EnumDemo.INSTANCE;
                System.out.println(instance + "==>" + instance.getId());
            }).start();
        }
    }
}

工厂模式