单例模式

了解一下

核心作用

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

常见的五种单例模式的是实现方式

  • 饿汉式(线程安全,调用效率高,不能延时加载)

  • 懒汉式(线程安全,调用效率不高,可以延时加载)

  • DCL懒汉式(由于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
33
34
35
36
37
38
39
40
41
42
43
44
45

//饿汉式单例模式

public class demo1 {

//1. 私有化构造器

private demo1(){

}

// 2. 类初始化的时候,立即加载该对象;由于用了static关键字,在类装载的时候就初始化对象,

// 不存在并发问题,因为一加载就出来了

private static demo1 instance=new demo1();

//3. 提供获取该对象的方法;由于不存在并发问题,所以没有加synchronized关键字,效率高;如果考虑到并发情况,是要加synchronized关键字的;

public static demo1 getInstance(){

return instance;

}

}

//问题:如果在这个类中,加了一些开辟空间的方法,那么不管我用不用这个类,这个块空间都被占用了,

//浪费了资源,理想情况应该是在调用getInstance方法的时候,再去开辟空间;

class demo1Test{

public static void main(String[] args) {

demo1 instance=demo1.getInstance();

demo1 instance1=demo1.getInstance();

System.out.println(instance==instance1);

}

}

懒汉式

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
44
45
46
47

public class demo2 {

//1. 私有化构造器

private demo2(){

}

//2. 类初始化的时候,不立即加载该对象

private static demo2 instance;

//3. 提供获取该对象的方法,由于可能会出现有多个线程来进来的话,需要让他们排队,否则都有问题

// 所以有synchronized同步这个关键字,效率低!

public static synchronized demo2 getInstance(){

if(instance==null)

{

instance=new demo2();

}

return instance;

}

}

class demo2Test{

public static void main(String[] args) {

demo2 instance=demo2.getInstance();

demo2 instance1=demo2.getInstance();

System.out.println(instance==instance1);

}

}

DCL懒汉式

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

//DCL懒汉式

public class demo3 {

private demo3(){

}

//2. 类初始化的时候,不立即加载该对象

private volatile static demo3 instance;

//volatile是后来加的

//3. 提供获取该对象的方法,由于可能会出现有多个线程来进来的话,需要让他们排队,否则都有问题

// 所以有synchronized同步这个关键字,效率低!

//不用synchronized可以用synchronized代码块,锁demo3这个类本身;双重检测

//分析:现在不需要对整个方法进行同步了,将锁的范围变得更精细了,如果有个进程进来了,发现

//这个instance对象没有被创建,有一个锁,他首先要和其他进程竞争本类的锁,获得锁之后,再次检查,

//如果还是null,说明他是第一个竞争到这个锁的,于是他这个线程就负责创建这个对象,其他的线程

//进来之后,直接调用即可

public static demo3 getInstance(){

if(instance==null)

{

synchronized (demo3.class){

if(instance==null)

{

instance=new demo3();

}

}

}

return instance;

}

}

//由于这个操作不是原子性操作,所以他会经过下面几个步骤

//1.分配内存

//2. 执行构造方法

//3. 执行地址

//可能会出现的问题:极端情况,一个线程进来了,走到instance=new demo3(),还没有出去方法,

//结果另一个线程进来了,就会直接走到 return instance;,这样instance就是一个新的对象,

//破环了单例模式,可能会发生一些意想不到的问题;在这种情况下加volatile关键字;

//volatile可以保证一个线程在对这个变量进行修改的时候,另一个线程,该变量的缓存就失效了,直接读内存中的值

class demo3Test{

public static void main(String[] args) {

demo3 instance=demo3.getInstance();

demo3 instance1=demo3.getInstance();

System.out.println(instance==instance1);

}

}

静态内部类实现

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
44
45
46
47
48
49

//静态内部类实现

public class demo4 {

private demo4(){

}

private static class InnerClass{

private static final demo4 instance=new demo4();

}

public static demo4 getInstance(){

return InnerClass.instance;

}

}

//反射机制可以破坏private

class demo4Test{

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

demo4 instance=demo4.getInstance();

//通过反射拿到instance

Constructor<demo4> demo4Constructor=demo4.class.getDeclaredConstructor(null);

demo4Constructor.setAccessible(true);

demo4 instance1=demo4Constructor.newInstance();

System.out.println(instance==instance1);

System.out.println(instance.hashCode());

System.out.println(instance1.hashCode());

}

}

优化静态内部类

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

public class demo5 {

private demo5(){

synchronized (demo5.class){

if(instance!=null)

{

throw new RuntimeException("不要试图用反射破坏单例模式!");

}

}

}

//2. 类初始化的时候,不立即加载该对象

private volatile static demo5 instance;

public static demo5 getInstance(){

if(instance==null)

{

synchronized (demo5.class){

if(instance==null)

{

instance=new demo5();

}

}

}

return instance;

}

}

class demo5Test{

public static void main(String[] args) throws Exception {

demo5 instance=demo5.getInstance();

//通过反射拿到instance

Constructor<demo5> demo4Constructor=demo5.class.getDeclaredConstructor(null);

demo4Constructor.setAccessible(true);

demo5 instance1=demo4Constructor.newInstance();

System.out.println(instance==instance1);

System.out.println(instance.hashCode());

System.out.println(instance1.hashCode());

}

}

枚举

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

//枚举

//反射不能够破坏枚举

public enum demo6 {

INTERFACE;

public demo6 getInstance(){

return INTERFACE;

}

}

class demo6Test{

public static void main(String[] args) {

demo6 anInterface = demo6.INTERFACE;

demo6 anInterface2 = demo6.INTERFACE;

System.out.println(anInterface==anInterface2);

}

}