Head First设计模式——单例模式
单例模式是所有设计模式中最简单的模式,也是我们平常经常用到的,单例模式通常被我们应用于线程池、缓存操作、队列操作等等。
单例模式旨在创建一个类的实例,创建一个类的实例我们用全局静态变量或者约定也能办到单例的作用,为什么我们要用单例模式?
接下来我们就从如何形成单例模式,单例模式创建的过程来讲解。
1、单例如何形成
我们平常创建一个对象需要new对象,假如有一个对象ObjectClass我们实例化它。
new ObjectClass()
如果另外一个类要使用ObjectClass则可以再通过new来创建另外一个实例化,如果这个类是public 则我们可以在使用的时候多次实例化对象。
那我们怎么保证类不被其他类实例化,利用private关键字我们可以采用私有构造函数来阻止外部实例化该类。
public class ObjectClass
{
private ObjectClass()
{
}
}
这样一来我们无法实例化ObjectClass则我们就无法使用它。那我们要怎么实例化呢?
由于私有构造方法我们只能在内部访问,所以我们可以用一个内部方法实例化ObjectClass,为了外部能够访问这个方法我们将这个方法设置成static。
这样做了之后确保返回对象始终是第一次创建的对象,我们用一个私有静态对象来存储实例化的对象,如果对象没创建我们则立即创建,如果已经创建就返回已经创建的对象。
public class ObjectClass
{
private static ObjectClass singleton;
private ObjectClass()
{
} public static ObjectClass GetSingletone()
{
if (singleton == null)
{
singleton = new ObjectClass();
}
return singleton;
}
}
到这里我们的单例模式就形成了,单例模式定义:
单例模式:确保一个类只有一个实例,并提供一个全局访问点。
2、多线程导致单例模式问题
启用多线程测试单例返回对象
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
TestSingleton();
}
Console.ReadKey();
} public static void TestSingleton()
{
Task.Factory.StartNew(new Action(() =>
{
var hc = ObjectClass.GetSingletone().GetHashCode();
Console.WriteLine(hc);
}));
}
}

如图中做的测试一样,我启了10个线程获得单例对象然后打印对象的HashCode。测试发现有HashCode不一致的情况,证明单例返回的对象并不是只有一个。
因为多线程运行的时候可能会同时进行if (singleton == null)的判断,如果此时singleton变量还没被实例化则可能有多个线程进入到实例化代码,以至于返回的实例化对象不是同一个。
3、解决多线程单例问题
由于多线程导致if检查变量问题,则争对检查问题我们可以有两类解决办法:
①"急切"创建实例,不用延迟实例化做法
急切实例化就是在静态初始化器中创建对象,这样就保证了程序运行阶段单例对象已经创建好,去除if判断。
public class ObjectClass
{
private static ObjectClass singleton=new ObjectClass();
private ObjectClass()
{
} public static ObjectClass GetSingletone()
{
return singleton;
}
}
②加锁
为了让创建对象只能有一个线程操作,则我们对创建对象代码进行加锁处理,再次改造GetSingletone方法。
public class ObjectClass
{
private static ObjectClass singleton = new ObjectClass();
private static object lockObj = new object();
private ObjectClass()
{
} public static ObjectClass GetSingletone()
{
lock (lockObj)
{
if (singleton == null)
{
singleton = new ObjectClass();
}
}
return singleton;
}
}
加锁对性能有一定的损耗,如果你的系统对性能要求比较高,我们对于加锁的处理还有一种优化方式:双重检查加锁
public static ObjectClass GetSingletone()
{
if (singleton == null)
{
lock (lockObj)
{
if (singleton == null)
{
singleton = new ObjectClass();
}
}
}
return singleton;
}
使用双重检查加锁,则多线程在运行的时候如果已经创建了单例对象后就不会再进入到lock代码段以此减少锁带来的性能损耗。
然后我们再来测试一波,启用50个线程,可以看到输出的HashCode是一致的。

4、总结
回到我们开始讲的为什么不用全局变量或者约定来解决单例问题,因为对于我们开发来说虽然有约定但是我们不能保证每个人都按照约定或者滥用全局变量造成问题。
而使用单例模式能进行更好的自我约定和管理,当然我们也有可能会滥用单例模式,这就需要对它能解决什么问题如何使用深入理解。
设计模式并不是要生搬硬套,而是在需要的时候符合的场景进行合理使用。
虽然单例模式比较简单,但通过分析我们看到问题也不少,要更好的使用需要我们更好的分析,也希望这篇博文对你有些帮助。
Head First设计模式——单例模式的更多相关文章
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
- 设计模式 单例模式(Singleton) [ 转载 ]
设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...
- c#设计模式-单例模式(面试题)
c#设计模式-单例模式 单例模式三种写法: 第一种最简单,但没有考虑线程安全,在多线程时可能会出问题, public class Singleton { private static Singleto ...
- java设计模式单例模式 ----懒汉式与饿汉式的区别
常用的五种单例模式实现方式 ——主要: 1.饿汉式(线程安全,调用率高,但是,不能延迟加载.) 2.懒汉式(线程安全,调用效率不高,可以延时加载.) ——其他: 1.双重检测锁式(由于JVM底层内部模 ...
- 最简单的设计模式——单例模式的演进和推荐写法(Java 版)
前言 如下是之前总结的 C++ 版的:软件开发常用设计模式—单例模式总结(c++版),对比发现 Java 实现的单例模式和 C++ 的在线程安全上还是有些区别的. 概念不多说,没意思,我自己总结就是: ...
- ES6教程-字符串,函数的参数,了解函数的arguments对象,js面向对象,设计模式-单例模式,解构赋值
前言 主要讲解了ES6对字符串的拓展,包括includes,startsWith和endsWith,另外增加了字符串模板. Start includes()是否包含 startsWith()以什么开头 ...
- Java设计模式の单例模式
-------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱 ...
- java设计模式——单例模式(一)
一. 定义与类型 定义:保证一个类仅有一个实例,并提供一个全局访问点 类型:创建型 二. 适用场景 想确保任何情况下都绝对只用一个实例 三. 优缺点 优点: 在内存里只有一个实例,减少了内存开销 可以 ...
- php实现设计模式————单例模式
php实现设计模式————单例模式 什么是单例模式 为什么要使用单例模式 php中有哪些方式实现新建一个对象实例 如何阻止这种实例化实现理想的单例模式 代码实现 什么是单例模式 为什么要使用单例模式 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
随机推荐
- hash长度扩展攻击
这里面就放一张百度百科的解释吧,emmm 反正我是看不懂还是做一下题来巩固一下吧 CTF中的hash长度攻击 进入网页你会发现页面显示  我这里没有看到什么可以利用的,抓了一下包也没有什么有可以利 ...
- LAMP环境部署物联网项目
今天来在LAMP环境下搭建一个PHP项目,开始之前,先来普及下物联网常识: 物联网,即Internet of Things,简写IOT.让所有能行使独立功能的普通物体实现互联互通的网络,通过物联网可以 ...
- 隐身衣揭秘--java中继承/隐藏/覆写
故事背景 看过<哈利·波特>的娃们,想必一定还记得电影中的“隐形斗篷”..这件隐形衣是哈利收到的圣诞礼物,也是死亡圣器中的三件套之一,它让哈利小盆友在执行任务的过程中简直是如虎添翼! 其实 ...
- SpringMVC流程源码分析及DispatcherServlet核心源码
一.源码分析前还是需要一张流程图作为指导,如下: 二.简单介绍以及源码定位 DispatcherServlet其实就是一个HttpServlet,他是HttpServlet的子类,所以它和普通的Htt ...
- 微信小程序学习总结
微信小程序开发环境安装以及相关设置配置 微信小程序前端页面书写 微信小程序前端样式WXSS书写 微信小程序中事件 微信小程序自定义组件 微信小程序发起请求 微信小程序登入流程 微信小程序路由跳转 微信 ...
- LSTM神经网络走读
0设计概述 RNN梯度爆炸和消失比较严重,RNN隐层只有一个状态h记录短期记忆,增加一个长期记忆状态c似乎就可以解决问题.
- formidable处理提交的表单或文件的简单介绍
一般来说,客户端向服务端提交数据有GET和POST这两种方式,在之前的文章node.js当中的http模块与url模块的简单介绍当中我们可以知道通过req.url与url模块的配合处理可以快速得到客户 ...
- Git上传项目至GitHub
要使用github,首先需要下载git.安装git 1.先在github上创建一个git仓库,复制该github路径 2.用cmd进入一个目录 git clone github路径 从github上直 ...
- [CODEVS3366] 矿石
题目描述 Description 经历了千辛万苦,小J找到了N块矿石.这些矿石都有毒性,但只要将两块矿石放在一起,再分开即可解毒.但任一两块矿石都可以互相吸引.为了降低吸引力,小J将他们放入一个直径仅 ...
- libevent::日志
LibEvent 能记录内部的错误和警告日志,如果编译进日志支持功能,也会记录调试信息.默认情况下这些消息都是输出 到 stderr,你也可以通过提供自己的日志函数的方法来覆盖这种行为. 为了覆盖 L ...