认识单例模式

饿汉模式

    提到"饿汉模式",相信很多人跟我一样一脸懵逼,为什么叫"饿汉"?为了清楚理解"饿汉模式",请允许我给你们介绍另一个概念: “立即加载”.什么是立即加载?立即加载就是我们在使用类的时候已经将对象创建完毕,常见的的实现方式就是直接new对象.而立即加载从语义来看,有着"急切,马上"的含义,所以称为"饿汉模式".

   "饿汉模式"是在调用方法之前,实例对象已经被创建.接下来用代码进行演示

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
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019/10/24
* 立即加载 == 饿汉模式
*/
public class SingletonTest {
/*
* static 关键字随着类的加载而加载,在内存中只有那一份;
* private关键字不允许外界进行访问
* */
private static SingletonTest singletonTest = new SingletonTest();

//不能通过new的方式创建对象
private SingletonTest(){

}

public static SingletonTest getInstance(){
/*
* 1.此版本代码的缺点是不能有其他实例变量;
* 2.getInstance()方法没有同步,有可能存在线程安全问题
* */
return singletonTest;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(SingletonTest.getInstance().hashCode());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MyRun {
public static void main(String[] args) {

MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();
m2.start();
m3.start();
}
}

懒汉模式

  延迟加载就是在调用get()方法时实例才被创建.常见方法就是在get方法中进行new对象.

单线程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private static MySingleton mySingleton;
private MySingleton(){

}

public static MySingleton getInstance(){
//延迟加载
if (mySingleton == null) {
mySingleton = new MySingleton();
}
return mySingleton;
}

}
1
2
3
4
5
6
7
8
9
10
11
12
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/25 16:40
*/
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(MySingleton.getInstance().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
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/25 16:41
*/
public class MyRun {

public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();
m2.start();
m3.start();
}

}
--------------------------------------------------
连接到目标 VM,地址:'127.0.0.1:51282', transport: 'socket'
767501637
767501637
767501637
断开与目标 VM 的连接,地址:'127.0.0.1:51282', transport: 'socket'
多线程
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
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private static MySingleton mySingleton;
private MySingleton(){

}

public static MySingleton getInstance(){
//延迟加载
if (mySingleton == null) {
try {
Thread.sleep(3000);
mySingleton = new MySingleton();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return mySingleton;
}

}
----------------------------------
453594667
704737505
1372676747

在多线程环境下,控制台打印了3个不同的hashcode值,说明创建了三个不同的对象,违背了单例模式的本意

优化方法

声明synchronize关键字

既然多个线程可以同时访问getInstance()方法,那么我们在这个方法上加把锁,在一段时间内只能有一个线程访问getInstance()方法.

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
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private static MySingleton mySingleton;

private MySingleton() {

}

/*
* 设置同步方法效率低,整个方法被上锁
*
* */
synchronized public static MySingleton getInstance() {
//延迟加载
try {
if (mySingleton == null) {
Thread.sleep(3000);
mySingleton = new MySingleton();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return mySingleton;
}

}
----------------------------------
1287094394
1287094394
1287094394

上述方式是同步运行的,下一个线程要取得对象,就必须等上一个线程释放锁之后,才能继续执行,.效率低下.继续改写代码解决这个缺点

同步代码块

    同步代码块可以针对某些重要的代码进行单独的同步,这样效率可以大幅度提升.

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.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private static MySingleton mySingleton;

private MySingleton() {

}
public static MySingleton getInstance() {
//延迟加载
try {
if (mySingleton == null) {
Thread.sleep(3000);
synchronized (MySingleton.class) {
mySingleton = new MySingleton();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return mySingleton;
}

}
-----------------------------
547686109
1391273746
738978376
使用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
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private static MySingleton mySingleton;

private MySingleton() {
}

public static MySingleton getInstance() {
//延迟加载
try {
if (mySingleton == null) {
Thread.sleep(3000);
synchronized (MySingleton.class) {
if (mySingleton == null) {
mySingleton = new MySingleton();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return mySingleton;
}
}

由于DCL无法解决指令重排的问题,需要关键字volatile禁止指令重排,在多线程环境下,使用DCL双检锁+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
package com.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
//内部类方式
private static class MyHolder {
private static MySingleton mySingleton = new MySingleton();
}

private MySingleton() {

}

public static MySingleton getInstance(){
return MyHolder.mySingleton;
}
}
------------------------
704737505
704737505
704737505
序列化与反序列化

未完待续…

使用static代码块

特性: 利用static 带块随着类的加载而执行且只执行一次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.xiebin.day5;


/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private static MySingleton instance = null;

private MySingleton(){

}

static {
instance = new MySingleton();
}

public static MySingleton getInstance(){
return instance;
}
}
使用枚举类

特性: 在使用枚举类时,构造方法会被自动调用

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.xiebin.day5;

/**
* @author 谢斌
* @create 2019-10-2019/10/24
*/
public class MySingleton {
private MySingleton(){

}
private enum Singleton{
INSTANCE;
private final MySingleton instance;
Singleton(){
instance = new MySingleton();
}
private MySingleton getInstance(){
return instance;
}
}
public static MySingleton getInstance(){
return Singleton.INSTANCE.getInstance();
}
}
坚持原创技术分享,您的支持将鼓励我继续创作!
-------------本文结束感谢您的阅读-------------
(>看完记得五星好评哦亲<)
0%