[工作中的设计模式]单例模式singleton
一、模式解析:
单例模式是最简单和最常用的设计模式,面试的时候,不管新毕业的学生还是已经工作多年的筒子,对单例模式基本都能聊上两句。单例模式主要体现在如下方面:
1、类的构造函数私有化,保证外部不能直接使用构造函数创建类的实例
2、提供获取实例的方法,外部可以通过此方法获取已经创建好的实例对象,
3、获取实例的方法必须保证实例唯一性。
4、根据获取实例方法保证唯一性的方式,单例模式又分为以下几种:
二、模式代码
1、懒汉模式
/**
* 单例模式-懒汉模式
* 懒汉模式意味着此模式比较懒惰,直到系统发起调用时候才会创建此对象的实例。
* 需要注意的是要保证线程的同步,防止出现多个实例创建的情况
* @author zjl
*
*/
public class Singleton1 {
//创建元素实例
private static Singleton1 singleton;
//构造对象置为私有变量,不能从外部进行创建
private Singleton1(){ }
/**
* public方法,在外部调用时候,获取此对象的实例
* 注意需要使用同步方法,防止多线程时候产生多实例
* @return 单例对象实例
*/
public static synchronized Singleton1 getInstance(){
if(singleton==null){
singleton=new Singleton1();
}
return singleton;
}
}
懒汉模式实现了对象的懒加载,不过缺点是由于getInstance上加了同步关键字,导致此方法只能有一个线程访问,效率会比较低
2、为了解决懒汉模式的速度问题,引入检测,也就是加锁之前先做一次判定,并将实例声明为volatile
public class Singleton {
private volatile static Singleton instance; //声明成 volatile
private Singleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
3、痴汉模式
/**
* 单例模式的痴汉模式
* 痴汉模式表示此模式很急,一旦对象初始化,立马创建一个实例,以后获得实例的都采用此实例
* @author zjl
*
*/
public class Singleton2 {
/**
* 私有构造函数,保证不能从外部创建实例
*/
private Singleton2(){}
//痴汉模式重点,对象初始化直接创建实例
private static Singleton2 singleton=new Singleton2(); /**
* 直接返回之前创建的实例
* @return 对象实例
*/
public static Singleton2 getInstance() {
return singleton;
}
}
痴汉模式不会存在线程同步问题,但是缺点是不是懒加载,对象创建后立马创建实例。
4、静态内部类
/**
* 使用静态内部类来创建单例
* @author zjl
*
*/
public class Singleton3 {
//私有化构造函数,使他不能在外部被创建
private Singleton3(){};
//创建静态内部类,初始化成员变量
private static class SingletonHodler{
private static final Singleton3 INSTANCE=new Singleton3();
}
/**
* 获取实例方法,执行时候才去创建实例
* @return
*/
public static final Singleton3 getInstance(){
return SingletonHodler.INSTANCE;
}
}
静态内部类创建的方法使用不多,也是在面试中很少了解到的,但却是最为推荐的方法,因为他实现了懒加载,线程安全且不需要依赖jdk版本。
三、应用场景
单例模式在框架中应用较多,比如spring的bean管理可以设置是否为单例模式,数据库对象实例化
四、场景代码
由于比较简单,略过。
五、疑问解决
为什么我们在做懒汉模式的双重检测的时候,需要将对象改变为使用volatile进行修饰,此处与java的内存模式有关,并涉及到synchronized和volatile的内存操作
1、java的内存模型主要分为主内存和工作内存,对应计算机的结构可以认为是计算机内存和cpu的高速缓存,对于任何主内存的数据进行操作时候,必须先将数据读取到工作内存形成一个备份,当对工作内存的数据操作完成后,将工作内存数据重新同步到主内存。
对于工作内存和主内存的操作主要为:读取过程-read,load,use,写入过程 assign、store、write,这六个操作均具有原子性,但整个流程不是原子性。因此为了保证可见性和原子性,增加了lock和unlock操作,主要针对主内存区数据的锁定,一旦主内存区数据被lock,表示此线程独占了变量,不可被其他线程更改

2、synchronized的作用主要有两点:程序临界区和内存同步。
程序临界区:是一段仅允许一个线程进行访问的程序,一旦一个线程进入临界区,另外线程进入临界区只能在临界区外进行等待,等临界区线程执行完毕后,其他线程开始争夺资源,胜利者进入临界区,其他线程继续等待。
内存同步:主要是主内存和工作内存的同步。进入临界区时,如果有工作内存的数据未被同步的主内存,则先进行同步,成为其他线程可见状态。工作内存的数据会被丢失,如果要使用,需要重新进行read和load操作。退出临界区时,将工作内存数据写入主内存,保证修改可见。
3、volatile的作用是保证数据可见性,对于任何工作内存的assign操作会立刻store和write到主内存中,同时使其他工作内存放弃原有持有数据。但是volatile不保证操作原子性。
4、具体分析可能出现的问题:
public class Singleton {
private static Singleton instance; //声明成 volatile
private Date date=new Date;
private Singleton (){}
public static Singleton getSingleton() { //
if (instance == null) { //
synchronized (Singleton.class) { //
if (instance == null) { //
instance = new Singleton(); //
}
} //6
}
return instance; //
}
public Date getDate(){
return date;
}
}
如上边的代码,我们将原有懒汉模式稍作修改,增加了date字段,模拟双线程A与B的创建过程

由于new Singleton()不是一个原子操作,程序在进入和出临界区时候,均会同步主内存的内容,除此之外B线程如果在3和6之间调用,就会发生A线程的工作线程内的内容不一定全部写入了主内存,假设此阶段instance写入了主内存,但是date没有写入,B线程将对data内容不可见,因此getDate将返回null。
如果对instance添加了volatile,那么针对instance的修改,随时b线程都是可见的。

[工作中的设计模式]单例模式singleton的更多相关文章
- 设计模式 单例模式(Singleton) [ 转载2 ]
设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...
- 设计模式 单例模式(Singleton) [ 转载 ]
设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...
- JAVA设计模式-单例模式(Singleton)线程安全与效率
一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- .net中的设计模式---单例模式
.net设计模式: Net设计模式实例之单例模式( Singleton Pattern) 一 : 单例模式的简介:(Brief Introduction) 单例模式(Singleton Patter ...
- [工作中的设计模式]享元模式模式FlyWeight
一.模式解析 Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是因为这样更能反映模式的用意.享元模式是对象的结构模式.享元模式以共享的方式高效地支持 ...
- [工作中的设计模式]原型模式prototype
一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.pro ...
- .net中的设计模式---单例模式,涉及lock的用法
.客户端代码 static void Main(string[] args) { Singleton singleton2 = Singleton.GetInstance(); Singleton s ...
- [工作中的设计模式]解释器模式模式Interpreter
一.模式解析 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 以上是解释器模式的类图,事实上我 ...
随机推荐
- Linux Shell常用快捷键
ctrl+a[A]:将光标移到命令行开头 ctrl+e[E]:将光标移到命令行结尾 ctrl+c[C]:强制终止命令执行 ctrl+u[U]:删除/剪切光标之前的所有字符 ctrl+y[Y]:粘贴ct ...
- JS 打印对象的方法
2 3 4 5 6 7 8 function writeObj(obj){ var description = ""; for(var i in obj){ var p ...
- alias命令(使用命令别名)
通过alias命令可以给一些命令定义别名,如,将长的难记住的命令起一个容易记住的别名,提高工作效率 alias -p 查看已有的别名列表 命名别名格式: alias 新命令名='原命令名 -参数/选项 ...
- DB2 嵌入式应用中定义游标(开放平台上)
DECLARE CURSOR statement The DECLARE CURSOR statement defines a cursor. Invocation Although an inter ...
- Asp.Net - 8.多线程
8.1 概念 进程(Process):是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源.进程之间是相对独立的,一个进程无法直接访问另一个进程的数据(除非利用分布式计算方式),一个 ...
- 【SQL】检索满足条件的最大值的数据集合
是不是看题目觉的看不懂?其实我自己也看不懂,但是又找不到更好的词来形容. 为了更好的表达我的意思,请看下. 如果有一张成绩表(Points), 学生(student) 成绩(point) 科目(sub ...
- win10下安装Ubuntu + 修复Ubuntu引导
如何在已安装 Windows 10 的情况下安装 Linux(Ubuntu 15.04)双系统? - Microsoft Windows - 知乎http://www.zhihu.com/questi ...
- Python 写Windows Service服务程序
1.需求 为什么要开发一个windows服务呢?之前做一个程序,必须要读取指定目录文件License, 因为其他程序也在读取这指定目录的License文件,且License不同时会修改License的 ...
- 【转载】 Python 方法参数 * 和 **
Python的函数定义中有两种特殊的情况,即出现*,**的形式. 如:def myfun1(username, *keys)或def myfun2(username, **keys)等. 他们与函数有 ...
- 攻城狮在路上(叁)Linux(二十九)--- 完整备份工具:dump以及restore
一.dump命令: 该命令既可以针对整个文件系统进行备份,也可以仅针对目录来备份.还可以指定不同的备份等级(-0~-9共10个等级). dump -W:列出在/etc/fstab中具有dump设置的分 ...