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

线程安全
方法一是线程安全的,在类被装载的时候,就初始化这个成员,Java库中Runtime就是用了这个方法。
方法二不是线程安全的。如果多个线程同时进入到函数中(临界区),那么会返回多个不同的实例。
代码验证
以下代码验证了线程不安全,即多线程的情况下方法二不能保证真正的“单例”。通过打印每个类的hashCode来显示,类实例不唯一。
class Singleton {
    private static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
        if (singleton == null) {
            try {
                Thread.sleep(233);
                singleton = new Singleton();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return singleton;
    }
}
public class Main {
    public static void main(String args[]) {
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {
                    Singleton singleton = Singleton.getInstance();
                    System.out.println(singleton.hashCode());
                }
            }).start();
        }
    }
}
解决方案
一种简单的处理方法是,将getInstance声明为synchronized的,效率低,因为每一个线程都在等待进入临界区。
另一种方法叫做Double Checked Locking(DCL),将公共变量声明为volatile(每次要使用这个变量的时候,从内存中取出来,保证各个线程可以看到这个变量的修改)。
class Singleton {
    private volatile static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
        if (singleton == null) {
            try {
                Thread.sleep(233);
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new Singleton();
                    }
                }
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return singleton;
    }
}
坏处
有啥坏处,别怪这个设计模式,都是全局变量的错。
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.
我想这位老兄说出这个观点,大概是用过异味的牙刷吧。为何不用规范来约束一同使用全局变量的人呢?
这位老兄道出了全局变量的本质,任何人可以用它做任何事,给整个程序带来了极大的不确定性。网上关于全局变量的坏处讨论很多,下面整理了一些,虽然并非都很坏:
- 代码耦合程度更高。假设一些函数依赖于全局变量的状态,这些函数通过这个全局变量联系到一起。一个函数的修改对另一个函数的读取存在影响,这些函数在无形中联系到了一起。
 - 给测试带来困难。全局变量存储了一些状态,需要安排好模块的运行顺序才可以正确的测试。可是,单元测试应该是相互独立的,而非有顺序的。
 - 多线程的写入的时候,要互斥。
 - 破坏了函数的输入输出功能。拿全局变量来读输入,写输出。
 - 命名冲突。
 - 可读性降低。要理解一个函数,还需要去跟踪使用到的全局变量的来龙去脉。
 
2, 破坏了单一职责原则
定义:就一个类而言,应该仅有一个引起它变化的原因
当一个类使用了Singleton的时候,它不仅负责这个类需要完成的任务,还负责这个单一对象资源的创建和管理。这个类的函数做两项任务,相关性低。
这一点算不上太坏。毕竟设计原则并不总是需要遵守的。
正确使用
上面讲了坏处,核心要义是避免将Singleton用作全局变量。Singleton的使用场景是什么呢?
回到Singleton的本质作用上来,只需要一个这个类的实例。前面讲这个设计模式破坏了单一职责原则,因为需要管理这个实例。
所以归结起来使用这个设计模式的两个原因:
(1) 单个实例
(2) 实例的管理
在[1]中讲了使用Singleton的一个具体例子。那就是日志(log)。因为log和代码的实质功能并不会产生耦合,所以是否开启log对于系统的功能没有太大的影响。
参考链接
- https://stackoverflow.com/questions/228164/on-design-patterns-when-should-i-use-the-singleton
 - https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil
 - https://stackoverflow.com/questions/26285520/implementing-singleton-with-an-enum-in-java
 - https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons?page=1&tab=votes#tab-top
 
【设计模式】Singleton的更多相关文章
- 单例设计模式Singleton之懒加载模式(懒汉模式)【原】
		
单例设计模式Singleton之懒加载模式(懒汉模式) SingletonLazy.java类 package kingtool; import kingtool.http.IPTool; publi ...
 - C++设计模式-Singleton
		
Singleton单例模式 Singleton 是对全局变量的取代策略作用:保证一个类只能有一个实例,并提供一个全局唯一的访问点. 仅有一个实例:通过类的静态成员变量来体现.提供访问它的全局访问点:访 ...
 - Java学习笔记——单例设计模式Singleton
		
单例设计模式:singleton 解决的问题: 确保程序在运行过程中,某个类的实例instance只有一份. 特点: 1 构造函数私有化 2 自己内部声明自己 3 提供一个public方法,负责实例化 ...
 - Java学习笔记(二十四):单例设计模式singleton
		
为什么要使用singleton设计模式? 假设设计了一个操作数组的工具类(Tools),里面有一个锤子的方法(Hammer),如果不使用singleton设计模式,每次想调用Hammer方法都需要ne ...
 - [C++设计模式] singleton 单例模式
		
这个设计模式主要目的是想在整个系统中仅仅能出现一个类的实例.这样做当然是有必定的.比方你的软件的全局配置信息,或者是一个Factory,或是一个主控类,等等. 你希望这个类在整个系统中仅仅能出现一个实 ...
 - 单例设计模式singleton
		
简单引入 单例设计模式作为最简单,最常用的设计模式.一般是这两中写法,这两种写法教科书所谓的标准写法,但是实际上存在不少问题.后面介绍标准写法,以规避这些问题. 1.懒汉式: /** * 问题在于,当 ...
 - Java设计模式—单例设计模式(Singleton Pattern)全然解析
		
转载请注明出处:http://blog.csdn.net/dmk877/article/details/50311791 相信大家都知道设计模式,听的最多的也应该是单例设计模式,这种模式也是在开发中用 ...
 - 使用C# (.NET Core) 实现单体设计模式 (Singleton Pattern)
		
本文的概念内容来自深入浅出设计模式一书 由于我在给公司做内培, 所以最近天天写设计模式的文章.... 单体模式 Singleton 单体模式的目标就是只创建一个实例. 实际中有很多种对象我们可能只需要 ...
 - javascript设计模式——Singleton
		
单例模式指的是只能被实例化一次. 推荐阅读: http://blog.mgechev.com/2014/04/16/singleton-in-javascript/ 比较通用的一种Singleton模 ...
 - C++设计模式-singleton单例模式_new
		
class nocopyable { protected: nocopyable(){}; virtual ~nocopyable(){}; nocopyable(const nocopyable ...
 
随机推荐
- 在 sql server 中批量删除表
			
通过查询系统表,可以批量获得 drop 语句,执行即可... select 'drop table '+name+';' from sys.tables
 - SQLMAP之tamper详解
			
sqlmap 是一款注入神器广为人知,里面的 tamper 常常用来绕过 WAF ,很实用的模块,但是却常常被新手忽略(比如我),今天就整理总结一下 tamper 的用法以及 tamper 的编写 P ...
 - 一次Ubuntu16系统的找回root密码的过程
			
一 背景知识介绍 ubuntn系统的默认配置,超级用户root的密码是随机的,如果没提前对root用户的密码进行设置, 是不能直接通过root的身份来进行某些操作的,必须使用sudo命令,并通过普通用 ...
 - 【LeetCode】746. 使用最小花费爬楼梯
			
使用最小花费爬楼梯 数组的每个索引做为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 cost[i](索引从0开始). 每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或 ...
 - 动态链接库(Dynamic Link Library)
			
DLL INTRODUCTION A DLL is a library that contains code and data that can be used by more than one pr ...
 - Could not resolve resource location pattern [classpath:com/****/mappers/*.xml]: class path resource [com/****/mappers/] cannot be resolved to URL because it does not exist的问题
			
这里建议先去看看路径的问题,看看application.xml的里面的导入的相应的配置文件的路径有没有问题,如下: 再者看看相应的注解有没有加上,service和controller等的注解 如果再不 ...
 - python详解json模块
			
我们在做工作中经常会使用到json模块,今天就简单介绍下json模块 什么是json JSON ,全称为JavaScript Object Notation, 也就是JavaScript 对象标记,它 ...
 - vue全局路由守卫beforeEach+token验证+node
			
在后端安装jsonwebtoken npm i jsonwebtoken --save 在 login.js文件中引入 // 引入jwtconst jwt = require ...
 - window系统下的pycharm对虚拟机中的Ubuntu系统操作MySQL数据库
			
问题:程序员和数据库的爱情故事:程序为了追一个叫MySQL数据库的姑娘,先苦练功夫,自己模拟泡妹过程积累经验,于是想到一个解决方法:[解决虚拟机跑需要连接数据库的程序卡的问题,通过在物理机Window ...
 - lua 8 字符串
			
转自:http://www.runoob.com/lua/lua-strings.html 字符串或串(String)是由数字.字母.下划线组成的一串字符. Lua 语言中字符串可以使用以下三种方式来 ...