一、模式解析:

单例模式是最简单和最常用的设计模式,面试的时候,不管新毕业的学生还是已经工作多年的筒子,对单例模式基本都能聊上两句。单例模式主要体现在如下方面:

  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的更多相关文章

  1. 设计模式 单例模式(Singleton) [ 转载2 ]

    设计模式 单例模式(Singleton) [ 转载2 ] @author java_my_life 单例模式的结构 单例模式的特点: 单例类只能有一个实例. 单例类必须自己创建自己的唯一实例. 单例类 ...

  2. 设计模式 单例模式(Singleton) [ 转载 ]

    设计模式 单例模式(Singleton) [ 转载 ] 转载请注明出处:http://cantellow.iteye.com/blog/838473 前言 懒汉:调用时才创建对象 饿汉:类初始化时就创 ...

  3. JAVA设计模式-单例模式(Singleton)线程安全与效率

    一,前言 单例模式详细大家都已经非常熟悉了,在文章单例模式的八种写法比较中,对单例模式的概念以及使用场景都做了很不错的说明.请在阅读本文之前,阅读一下这篇文章,因为本文就是按照这篇文章中的八种单例模式 ...

  4. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

  5. .net中的设计模式---单例模式

    .net设计模式: Net设计模式实例之单例模式( Singleton Pattern)  一 : 单例模式的简介:(Brief Introduction) 单例模式(Singleton Patter ...

  6. [工作中的设计模式]享元模式模式FlyWeight

    一.模式解析 Flyweight在拳击比赛中指最轻量级,即“蝇量级”或“雨量级”,这里选择使用“享元模式”的意译,是因为这样更能反映模式的用意.享元模式是对象的结构模式.享元模式以共享的方式高效地支持 ...

  7. [工作中的设计模式]原型模式prototype

    一.模式解析 提起prototype,最近看多了js相关的内容,第一印象首先是js的原型 var Person=function(name){ this.name=name; } Person.pro ...

  8. .net中的设计模式---单例模式,涉及lock的用法

    .客户端代码 static void Main(string[] args) { Singleton singleton2 = Singleton.GetInstance(); Singleton s ...

  9. [工作中的设计模式]解释器模式模式Interpreter

    一.模式解析 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 以上是解释器模式的类图,事实上我 ...

随机推荐

  1. Linux Shell常用快捷键

    ctrl+a[A]:将光标移到命令行开头 ctrl+e[E]:将光标移到命令行结尾 ctrl+c[C]:强制终止命令执行 ctrl+u[U]:删除/剪切光标之前的所有字符 ctrl+y[Y]:粘贴ct ...

  2. JS 打印对象的方法

      2 3 4 5 6 7 8 function writeObj(obj){  var description = "";  for(var i in obj){   var p ...

  3. alias命令(使用命令别名)

    通过alias命令可以给一些命令定义别名,如,将长的难记住的命令起一个容易记住的别名,提高工作效率 alias -p 查看已有的别名列表 命名别名格式: alias 新命令名='原命令名 -参数/选项 ...

  4. DB2 嵌入式应用中定义游标(开放平台上)

    DECLARE CURSOR statement The DECLARE CURSOR statement defines a cursor. Invocation Although an inter ...

  5. Asp.Net - 8.多线程

    8.1 概念 进程(Process):是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源.进程之间是相对独立的,一个进程无法直接访问另一个进程的数据(除非利用分布式计算方式),一个 ...

  6. 【SQL】检索满足条件的最大值的数据集合

    是不是看题目觉的看不懂?其实我自己也看不懂,但是又找不到更好的词来形容. 为了更好的表达我的意思,请看下. 如果有一张成绩表(Points), 学生(student) 成绩(point) 科目(sub ...

  7. win10下安装Ubuntu + 修复Ubuntu引导

    如何在已安装 Windows 10 的情况下安装 Linux(Ubuntu 15.04)双系统? - Microsoft Windows - 知乎http://www.zhihu.com/questi ...

  8. Python 写Windows Service服务程序

    1.需求 为什么要开发一个windows服务呢?之前做一个程序,必须要读取指定目录文件License, 因为其他程序也在读取这指定目录的License文件,且License不同时会修改License的 ...

  9. 【转载】 Python 方法参数 * 和 **

    Python的函数定义中有两种特殊的情况,即出现*,**的形式. 如:def myfun1(username, *keys)或def myfun2(username, **keys)等. 他们与函数有 ...

  10. 攻城狮在路上(叁)Linux(二十九)--- 完整备份工具:dump以及restore

    一.dump命令: 该命令既可以针对整个文件系统进行备份,也可以仅针对目录来备份.还可以指定不同的备份等级(-0~-9共10个等级). dump -W:列出在/etc/fstab中具有dump设置的分 ...