java单例模式 uml_Java 设计模式之单例模式(一) | 月光中的污点

Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89

一、背景

没有太多原由,纯粹是记录和总结自己从业以来经历和学习的点点滴滴。

本篇内容为 Java 设计模式系列的第一篇。

二、简单介绍

2.1 定义

单例模式是一种对象创建型模式,保证一个类只有一个实例,并且提供能对该实例加以访问的全局方法。

2.2 应用场景

操作系统的任务管理器

读取配置文件的类

数据库连接池

Javaweb 中的 Servlet 实例

Spring 创建的实例,默认为单例

...

三、实现方式

常用的实现方式有饿汉式、懒汉式和枚举类。

本篇文章主要讲饿汉式和懒汉式的单例模式。

共同点:将构造方法私有化,并且提供一个公共的方法访问该类的实例对象。

我们以任务管理器为例进行演示。

3.1 饿汉式

1

2

3

4

5

6

7

8

9

10

11

12public class TaskManager {

private static TaskManager tm = new TaskManager();

private TaskManager() {

}

public static TaskManager getInstance() {

return tm;

}

}

优点:线程安全,不用加同步锁,因此在高并发时调用效率高。

缺点:不能懒加载,如果不使用该类的实例,浪费内存资源。

3.2 懒汉式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16public class TaskManager {

private static TaskManager tm;

private TaskManager() {

}

public static synchronized TaskManager getInstance() {

if (tm == null) {

tm = new TaskManager();

}

return tm;

}

}

优点:实现懒加载,合理利用系统资源。

缺点:需要添加同步锁,高并发时调用效率不高。

注意点:上边的懒汉式可以通过反射机制创建多个实例。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18public class Client {

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

Class> clazz = Class.forName("com.light.gof.singleton.TaskManager");

Constructor> constructor = clazz.getDeclaredConstructor(null);

// 跳过检测机制

constructor.setAccessible(true);

TaskManager tm1 = (TaskManager) constructor.newInstance();

TaskManager tm2 = (TaskManager) constructor.newInstance();

System.out.println(tm1 == tm2);// 结果返回 false

}

}

3.3 优化方式

将饿汉式和懒汉式的优点集中起来。

1

2

3

4

5

6

7

8

9

10

11

12

13

14public class TaskManager {

private TaskManager() {

}

private static class InnerTaskManager {

private static final TaskManager tm = new TaskManager();

}

public static TaskManager getInstance() {

return InnerTaskManager.tm;

}

}

外部类没有静态属性,因此不会像饿汉式立即加载对象。

只有当调用公共方法(getInstance)时,才会加载静态内部类。加载内部类的过程是线程安全的。

内部类中通过 static final 确保内存中只有一个外部类的实例,因为实例变量(tm)只能被赋值一次。

四、UML 类图

类图表现如下:

Java-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89

五、性能比较

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

31public class Client {

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

// 线程数

int num = 10;

// 计数器

CountDownLatch cd = new CountDownLatch(num);

long t1 = System.currentTimeMillis();

for (int i = 0; i < num; i++) {

new Thread(new Runnable() {

@Override

public void run() {

for (int i = 0; i < 10000; i++) {

// 此处替换不同实现方式的单例代码进行测试

TaskManager tm = TaskManager.getInstance();

}

cd.countDown();

}

}).start();

}

// 主线程等待

cd.await();

System.out.println("耗时:" + (System.currentTimeMillis() - t1) + "ms");

}

}

测试结果:

实现方式

耗时

饿汉式

3ms

懒汉式

12ms

内部类方式

4ms

测试结果是相对的,硬件配置不同,测试结果不同,但是对于这个 3 种实现方式,它们的用时比例应该大致相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值