应用

资源管理器

属性管理器

多例是为了存储数据的,单例是为了共享可以减少资源消耗

饿汉式–JVM保证线程安全(简单推荐)

因为JVM可以保证每个类只加载一次,所以设置为static就可以保证对象只会被new一次,static是在加载链接初始化中的初始化就会赋值的,只执行一次。

多线程都没关系

但是用到与否,类加载就已经实例化

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
package com.deltaqin.designPattern.d01_singleton;

/**
* @author deltaqin
* @date 2021/3/26 1:39 下午
*/
public class Single01 {

private static final Single01 INSTANCE = new Single01();

public static Single01 getInstance() {
return INSTANCE;
}

public void m() {
System.out.println("hi");
}


public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()->{
System.out.println(getInstance().hashCode());
}).start();
}
}
}

懒汉式–自己保证线程安全

锁加在方法:效率变低

方法内部的代码块:只加在需要的时候,

需要双重验证,否则多个线程进入第一个 if 的时候,其他线程先执行获得锁初始化,自己就重复初始化了。

volatile防止重排序,获得空的对象

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
38
39
40
41
42
43
package com.deltaqin.designPattern.d01_singleton;

/**
* @author deltaqin
* @date 2021/3/26 1:42 下午
*/
public class Single02 {

// volatile 避免重排序
private static volatile Single02 instance;

// 居然没有加static,我说怎么不能直接调用
public static Single02 getInstance1() {
if (instance == null) {

// 测试后睡眠保证在线程睡眠的时候其他线程可以进来获得锁并且初始化暴露错误
try{
Thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
}

//instance = new Single02();

// 使用类锁,保证唯一
synchronized (Single02.class ) {
if (instance == null) {
instance = new Single02();
}
}
}
return instance;
}

public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> {
System.out.println(Single02.getInstance1().hashCode());
}).start();
}
}
}

(最好)静态内部类–懒加载+JVM保证只有一个实例

加载外部类时不会加载内部类

静态内部类以及静态属性保证了只有一个实例产生

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
package com.deltaqin.designPattern.d01_singleton;

/**
* JVM 的双亲委派保证类只加载一次,保证懒加载内部子类
*
* 静态内部类
* @author deltaqin
* @date 2021/3/26 2:10 下午
*/
public class Single03 {

// 子类不会在父类初始化的时候就初始化,而是在调用属性的时候才会
private static class Single {
// static类保证只加载一次
// final 只能被赋值一次
// 内部类保证了只在调用的时候才会初始化,懒加载
public static final Single03 instance = new Single03();
}

public static Single03 getInstance() {
return Single.instance;
}

public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> {
System.out.println(Single03.getInstance().hashCode());
}).start();
}
}
}

enum 枚举单例

优点:实现简单,枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!

缺点:无延迟加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.deltaqin.designPattern.d01_singleton;

/**
*
* 不仅可以解决线程同步,还可以防止反序列化。
*
* 反射可以通过class文件加载到内存,通过反序列化构建对象
* 枚举单例没有构造方法不会被反序列化,反编译之后是 abstract class
*
* @author deltaqin
* @date 2021/3/26 2:18 下午
*/
public enum Single04 {
INSTANCE;

public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> {
System.out.println(Single04.INSTANCE.hashCode());
}).start();
}
}
}