【设计模式 - 2】之单例模式(Singleton)
1、模式简介
单例模式在代码中是非常常用的,如线程池、数据库连接、注册表、共享资源、一些非常消耗资源的组件,等等。
单例模式主要解决如下问题:
- 确保一个特殊类的实例是独一无二的;
- 确保这个类的实例非常容易访问(提供了这个类的一个全局访问指针);
以下情况下可以使用单例模式:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时;
- 当要创建的对象非常耗费资源,而且全局用到了很多次时;
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
2、实现方法
2.1、整体思路
单例模式的整体思路如下:
- 在单例类中创建一个私有的本类对象;
- 将单例类的构造方法私有化,即用private修饰;
- 创建一个静态的方法getInstance(),返回本类类型的变量,在这个方法中返回单例。
根据这种思路,单例模式分为饿汉式单例和懒汉式单例。
2.2、饿汉式
饿汉式单例即在声明实例对象的时候就直接创建对象,在需要引用的时候直接引用。
饿汉式单例的简单代码如下:
/**
* 饿汉式单例的单例类
*/
public class HungerSingleton {
// 在声明单例变量的时候直接初始化
private static HungerSingleton instance = new HungerSingleton(); // 私有化构造方法,避免外界直接访问
private HungerSingleton() {
System.out.println("我是饿汉式单例的唯一实例!");
} // 公共的静态方法,用于外界调用该方法来创建唯一的对象
public static HungerSingleton getInstance() {
return instance;
} public void introduce() {
System.out.println("我是introduce()方法,我被调用了!" + Thread.currentThread().getName());
}
}
测试代码:
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
HungerSingleton.getInstance().introduce();
}
}).start();
}
}
运行结果:
从代码和运行结果来看,我们在五个线程中分别调用了HungerSingleton.getInstance()方法来创建对象,但HungerSingleton类的构造方法只被调用了一次,也就是说,五次调用只创建了一个对象。可以看出,饿汉式单例不仅保证了只创建一个对象,也保证了线程安全。
综上,饿汉式单例的优点是线程安全的,即无论在多少个线程中调用这个对象,调用的都是事先声明好的;缺点是声明出来的对象可能很占用系统资源,会在调用之前影响系统的工作状态。
2.3、懒汉式
懒汉式单例即在声明的时候不创建对象,在需要引用的时候先创建对象,然后引用。
懒汉式单例的简单代码如下:
public class LazySingleton {
// 声明变量,但不初始化
private static LazySingleton instance;
// 私有化构造方法,避免外界直接访问
private LazySingleton() {
System.out.println("我是懒汉式单例类的对象,我是唯一的!");
}
// 公共的静态方法,用于外界调用该方法来创建唯一的对象
public static LazySingleton getInstance() {
// 在调用对象的时候判断对象是否存在,如果存在则直接使用,否则才创建对象
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
public void introduce() {
System.out.println("我是introduce()方法,我被调用了!" + Thread.currentThread().getName());
}
}
测试代码和饿汉式单例的测试代码基本相同,也是五个线程同时访问。运行结果如下:
从代码和运行结果来看,当有多个线程同时访问这个单例对象的时候,就有可能创建出多个对象,这不符合单例模式的初衷。为了解决这个方法,我们需要对懒汉式单例进行一定的改进,方法是为创建单例的代码加一层双重检查锁。具体代码如下:
// 公共的静态方法,用于外界调用该方法来创建唯一的对象
public static LazySingleton getInstance() {
// 在调用对象的时候判断对象是否存在,如果存在则直接使用,否则才创建对象
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
再次运行,运行结果如下:
综上,懒汉式单例的优点是在没有调用单例对象之前不会影响系统的工作状态;缺点是线程不安全,即如果在多个线程中同时访问这个单例对象,很可能会创建出多个对象,但这种缺点是可以改进的,方法就是加双重检查锁。
3、模式优缺点
单例模式有以下优点:
- 使用单例模式可以严格的控制用户怎样以及如何访问它;
- 节约系统资源,对于一些需要频繁创建和销毁的对象,单例模式可以提高系统的性能;
- 允许可变数目的实例。
单例模式有以下缺点:
- 单例模式的扩展有很大的困难;
- 单例类职责过重,在一定程度上违背了“单一职责原则”;
- 滥用单例模式可能产生一些负面问题,如一个对象的访问过多可能引起对象溢出;如果实例化的对象长时间不被使用,系统会默认为是垃圾而回收,导致对象状态的丢失。
4、多实例单例
前面说过,单例可以控制生成的对象的个数或不同种类的对象。例如,一个单例类可能会有多个子类,这些子类都是对父类的扩展,每个子类都有其独特的作用,那么我们可能就会控制每个子类都可以有一个单例对象,这时就用到了多实例单例。
其实想想也不奇怪,我们既然能控制一个类只能生成一个对象,那么控制这个类生成固定个数个对象自然也不成问题。要解决这个问题,我们需要用到集合类,Map、List都可以,每当需要创建一个对象的时候,我们就遍历这个集合,判断这个对象在集合中有没有单例,如果有,则直接调用集合中的对象;如果没有,才可以创建一个对象然后存到集合中。
当然,这种多实例单例的方法也不仅用在每个类只能创建一个实例的情况,也可以用在一个类可以创建多个相同的实例的情况,其解决方法依然是使用集合,具体方法如上,就不多说了。
最后贴出单例模式的GitHub地址:【GitHub - Singleton】。
【设计模式 - 2】之单例模式(Singleton)的更多相关文章
- 【白话设计模式四】单例模式(Singleton)
转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...
- IOS设计模式浅析之单例模式(Singleton)
说在前面 进入正式的设计模式交流之前,扯点闲话.我们在项目开发的过程中,经常会不经意的使用一些常见的设计模式,如单例模式.工厂方法模式.观察者模式等,以前做.NET开发的时候,认真拜读了一下程杰老师的 ...
- 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性
模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...
- Net设计模式实例之单例模式( Singleton Pattern)
一.单例模式简介(Brief Introduction) 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯 ...
- JavaScript设计模式 Item 6 --单例模式Singleton
单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如线程池.全局缓存.浏览器的window对象.在js开发中,单例模式的 ...
- 设计模式系列之单例模式(Singleton Pattern)
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.这种模式涉及到一个单一的类,该类负责创建自己的对象 ...
- Python设计模式 - 创建型 - 单例模式(Singleton) - 十种
对于很多开发人员来说,单例模式算是比较简单常用.也是最早接触的设计模式了,仔细研究起来单例模式似乎又不像看起来那么简单.我们知道单例模式适用于提供全局唯一访问点,频繁需要创建及销毁对象等场合,的确方便 ...
- 我的设计模式学习笔记------>单例模式(Singleton)
一.前言 有些时候,允许自由创建某个类的实例是没有意义,还可能造成系统性能下降(因为创建对象所带来的系统开销问题).例如整个Windows系统只有一个窗口管理器,只有一个回收站等.在Java EE应用 ...
- 设计模式之三:单例模式singleton
单例设计模式确切的说就是一个类只有一个实例,有一个全局的接口来访问这个实例.当第一次载入的时候,它通常使用延时加载的方法创建单一实例. 提示:苹果大量的使用了这种方法.例子:[NSUserDefaul ...
- 《JAVA设计模式》之单例模式(Singleton)
在阎宏博士的<JAVA与模式>一书中开头是这样描述单例模式的: 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 单例模式的 ...
随机推荐
- [学习笔记]设计模式之Facade
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Facade(外观)模式定义了一个高层接口,它能为子系统中的一组接口提供一个一致的界面,从而使得这一子系统更加容易使用.欢迎回到时の魔 ...
- mysql 中执行的 sql 注意字段之间的反向引号和单引号
如下的数据表 create table `test`( `id` int(11) not null auto_increment primary key, `user` varchar(100) no ...
- js 中文排序
/** * 比较函数 * @param {Object} param1 要比较的参数1 * @param {Object} param2 要比较的参数2 * @return {Number} 如果pa ...
- ios开发之C语言第一天
最近在学习ios开发,先学习C语言,再学习OC和swift.正所谓"万丈高楼平地起",打好基础是很重要的,所以C语言也必须好好学习.学习中所使用的操作系统是OS X,开发工具是Xc ...
- 用三或四个个div标签实现工字效果
使用重构的方式制作出一个如下图的水平.垂直都居中,短边为50px,长边为150px的红色“工”字. a) 使用3个div完成 <!DOCTYPE html><html lang=&q ...
- Mac上RoR环境搭建问题
昨天一晚上折磨的我啊都快疯掉了. 按照http://railstutorial-china.org方法配制,到rvm requirements这个命令执行时就麻烦事来了. WutekiMacBook- ...
- 优先级和lisp式前缀表达式
if(f(a,b) > 0) # 1 if(f(a, b>0)) # 2 上面在调用f的时候,1使用括号表明了参数的优先级,不然会出现2这样的情况. 而使用lisp式的前缀表达式则不会有不 ...
- [BZOJ 1034] [ZJOI2008] 泡泡堂BNB 【贪心】
题目链接:BZOJ - 1034 题目分析 这道题和田忌赛马的典故很相似. 先要将两队的队员都按照水平从小到大分别排序. 然后每次尝试用我方最弱的队员赢对方最弱的队员,或者用我方最强的队员赢对方最强的 ...
- ios7新特性3-Map Kit新特性
Map Kit 框架 (MapKit.framework) 包含了大量的改进以及为基于地图的程序提供了新特性.利用地图显示位置信息的应用现在可以使用Maps这个程序用到的3D地图,包括控制程序控制视线 ...
- SpringMVC控制器接收不了PUT提交的参数的解决方案
摘要: SpringMVC控制器接收不了PUT提交的参数的解决方案 这次改造了下框架,把控制器的API全部REST化,不做不知道,SpringMVC的REST有各种坑让你去跳,顺利绕过它们花了我不少时 ...