经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
【设计模式】Singleton
来源:cnblogs  作者:楷哥  时间:2019/7/24 8:54:29  对本文有异议

前言

Singleton设计模式,确保全局只存在一个该类的实例。将构造器声明为private,防止调用(虽然还是可以使用反射来调用)。声明一个静态的类实例在类中,声明一个公共的获取实例的方法。这篇博文给出了简单的实现方法,分析如何做到线程安全,整理了使用Singleton的坏处。

线程安全

方法一是线程安全的,在类被装载的时候,就初始化这个成员,Java库中Runtime就是用了这个方法。
方法二不是线程安全的。如果多个线程同时进入到函数中(临界区),那么会返回多个不同的实例。

代码验证

以下代码验证了线程不安全,即多线程的情况下方法二不能保证真正的“单例”。通过打印每个类的hashCode来显示,类实例不唯一。

  1. class Singleton {
  2. private static Singleton singleton;
  3. private Singleton() {}
  4. public static Singleton getInstance() {
  5. if (singleton == null) {
  6. try {
  7. Thread.sleep(233);
  8. singleton = new Singleton();
  9. }
  10. catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. return singleton;
  15. }
  16. }
  17. public class Main {
  18. public static void main(String args[]) {
  19. for (int i = 0; i < 10; i++) {
  20. new Thread(new Runnable() {
  21. public void run() {
  22. Singleton singleton = Singleton.getInstance();
  23. System.out.println(singleton.hashCode());
  24. }
  25. }).start();
  26. }
  27. }
  28. }

解决方案

一种简单的处理方法是,将getInstance声明为synchronized的,效率低,因为每一个线程都在等待进入临界区。

另一种方法叫做Double Checked Locking(DCL),将公共变量声明为volatile(每次要使用这个变量的时候,从内存中取出来,保证各个线程可以看到这个变量的修改)。

  1. class Singleton {
  2. private volatile static Singleton singleton;
  3. private Singleton() {}
  4. public static Singleton getInstance() {
  5. if (singleton == null) {
  6. try {
  7. Thread.sleep(233);
  8. synchronized (Singleton.class) {
  9. if (singleton == null) {
  10. singleton = new Singleton();
  11. }
  12. }
  13. }
  14. catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. return singleton;
  19. }
  20. }

坏处

有啥坏处,别怪这个设计模式,都是全局变量的错。

1, 全局变量

Singleton管理公共的资源,在代码中的任何地方,只要调用Singleton的获取实例的方法,就可以获取到Singleton的实例,可以对这些公共资源的读写。这让Singleton成为了一个全局变量。

全局变量的坏处,也是Singleton的坏处。全局变量自有全局变量的使用情景和优点,要分开两面看待,这里只讲坏处并非想说全局变量一无是处。

在分析全局变量的坏处之前,在[2]下面看到了一个特别有意思的比喻:

Using global state with your fellow programmers is like using the same toothbrush with your friends - you can but you never know when someone will decide to shove it up his bum.

我想这位老兄说出这个观点,大概是用过异味的牙刷吧。为何不用规范来约束一同使用全局变量的人呢?

这位老兄道出了全局变量的本质,任何人可以用它做任何事,给整个程序带来了极大的不确定性。网上关于全局变量的坏处讨论很多,下面整理了一些,虽然并非都很坏:

  1. 代码耦合程度更高。假设一些函数依赖于全局变量的状态,这些函数通过这个全局变量联系到一起。一个函数的修改对另一个函数的读取存在影响,这些函数在无形中联系到了一起。
  2. 给测试带来困难。全局变量存储了一些状态,需要安排好模块的运行顺序才可以正确的测试。可是,单元测试应该是相互独立的,而非有顺序的。
  3. 多线程的写入的时候,要互斥。
  4. 破坏了函数的输入输出功能。拿全局变量来读输入,写输出。
  5. 命名冲突。
  6. 可读性降低。要理解一个函数,还需要去跟踪使用到的全局变量的来龙去脉。

2, 破坏了单一职责原则

定义:就一个类而言,应该仅有一个引起它变化的原因

当一个类使用了Singleton的时候,它不仅负责这个类需要完成的任务,还负责这个单一对象资源的创建和管理。这个类的函数做两项任务,相关性低。

这一点算不上太坏。毕竟设计原则并不总是需要遵守的。

正确使用

上面讲了坏处,核心要义是避免将Singleton用作全局变量。Singleton的使用场景是什么呢?

回到Singleton的本质作用上来,只需要一个这个类的实例。前面讲这个设计模式破坏了单一职责原则,因为需要管理这个实例。

所以归结起来使用这个设计模式的两个原因:

(1) 单个实例

(2) 实例的管理

在[1]中讲了使用Singleton的一个具体例子。那就是日志(log)。因为log和代码的实质功能并不会产生耦合,所以是否开启log对于系统的功能没有太大的影响。

参考链接

  1. https://stackoverflow.com/questions/228164/on-design-patterns-when-should-i-use-the-singleton
  2. https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil
  3. https://stackoverflow.com/questions/26285520/implementing-singleton-with-an-enum-in-java
  4. https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons?page=1&tab=votes#tab-top

原文链接:http://www.cnblogs.com/zhouzekai/p/11233617.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号