单例模式的定义

一个类有且仅有一个实例,并且自行实例化向整个系统提供。比如,多程序读取一个配置文件时,建议配置文件时,建议配置文件封装成对象。会方便操作其中的数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。

单例模式的作用

简单说来,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。

单例模式的类图

如何保证对象的唯一性

思想:(1)不让其他程序创建该类对象;

   (2)在本类中创建一个本类对象;

    (3)对外提供方法,让其他程序获取这个对象;

步骤:(1)因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类的对象;

   (2)就在类中创建一个本类的对象;

    (3)定义一个方法,返回该对象,让其他程序可以通过方法得到本类的对象(作用:可控,本类对象的产生由自己来决定,别谁想new就new)

【温情提示】:我们如果把代码写成这样,用户端获取到getInstance方法不是也可以获取很多的类对象吗?这不就不是单例了吗?

public class Car {

    private Car(){

    }
public static Car getInstance(){
return new Car();
}
}

所以我们直接自己在类中自己创建一个对象,getInstance方法只负责把对象返回给调用者,完全实现单例可控(你能获取我的方法,但是能拿到的是我自己创建好的对象)

public class Car {

    private static Car car=new Car();
private Car(){ }
public static Car getInstance(){
return car;
}
}

代码体现:

   (1)私有化构造函数

    (2)创建私有并静态的本类的对象

   (3)定义公有并静态的方法,返回该对象;

代码实现主要有两种方式:饿汉模式和懒汉模式

饿汉模式:类加载的时候对象就已经存在,饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变。

public class Single {

    private static Single s=new Single();
private Single(){ }
public static Single getInstance(){
return s;
}
}

懒汉模式:类加载的时候对象还不存在,就是所谓的延迟加载方式,需要时再进行创建,懒汉式若在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的

public class Single {

    private static Single single = null;

    private Single() {

    }

    public static Single getInstance() {
if (single == null) {
single = new Single();
}
return single;
}
}

下面我们解释一下,懒汉式的线程不安全性,通常情况下,我们建议写饿汉式,因为是线程安全的。
当多线程访问懒汉式时,因为懒汉式的方法内对共性数据进行多条语句的操作。

两个线程,线程一和线程二同时调用了getInstance方法,当线程1执行了if判断,single为空,还没来得及执行single =new Single()创建对象,这个时候线程2就来了,它也进行if判断,single依然为空,则创建Single对象,此时,两个线程就会创建两个对象,违背我们单例模式的初衷,如何解决呢?

出现线程安全的问题,为了解决这种问题,加入同步机制(不熟悉同步机制的请自行百度吧):静态同步函数的锁是类的字节码文件对象

  

这样一种设计可以保证只产生一个实例,并且只会在初始化的时候加同步锁,看似精妙绝伦,但却会引发另一个问题,这个问题由指令重排序引起。(这一部分来自:http://blog.csdn.net/zhangzeyuaaa/article/details/42673245)

指令重排序是为了优化指令,提高程序运行效率。指令重排序包括编译器重排序和运行时重排序。JVM规范规定,指令重排序可以在不影响单线程程序执行结果前提下进行。例如 instance = new Singleton() 可分解为如下伪代码:

memory = allocate();   //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址

但是经过重排序后如下:

memory = allocate();   //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址
//注意,此时对象还没有被初始化!
ctorInstance(memory); //2:初始化对象

将第2步和第3步调换顺序,在单线程情况下不会影响程序执行的结果,但是在多线程情况下就不一样了。线程A执行了instance = memory(这对另一个线程B来说是可见的),此时线程B执行外层 if (instance == null),发现instance不为空,随即返回,但是得到的却是未被完全初始化的实例,在使用的时候必定会有风险,这正是双重检查锁定的问题所在!

在JDK1.5之后,可以使用volatile变量禁止指令重排序:

代码实现:

public class Singleton {
private static volatile Singleton instance; private Singleton() {
} public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

volatile的另一个语义是保证变量修改的可见性。

好,到这里,就真正的把单例模式介绍完了,在此呢再总结一下单例类需要注意的几点:

一、单例模式是用来实现在整个程序中只有一个实例的。

二、单例类的构造函数必须为私有,同时单例类必须提供一个全局访问点。

三、单例模式在多线程下的同步问题和性能问题的解决。

四、懒汉式和饿汉式单例类。


本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/ysw-go/
1、本博客的原创原创文章,都是本人平时学习所做的笔记,如有错误,欢迎指正。
2、如有侵犯您的知识产权和版权问题,请通知本人,本人会即时做出处理文章。
3、本博客的目的是知识交流所用,转载自其它博客或网站,作为自己的参考资料的,感谢这些文章的原创人员

Java设计模式系列之单例模式的更多相关文章

  1. Java设计模式之《单例模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6510196.html 所谓单例,指的就是单实例,有且仅有一个类实例,这个单例不应该 ...

  2. Java设计模式系列-抽象工厂模式

    原创文章,转载请标注出处:https://www.cnblogs.com/V1haoge/p/10755412.html 一.概述 抽象工厂模式是对工厂方法模式的再升级,但是二者面对的场景稍显差别. ...

  3. Java设计模式系列-工厂方法模式

    原创文章,转载请标注出处:<Java设计模式系列-工厂方法模式> 一.概述 工厂,就是生产产品的地方. 在Java设计模式中使用工厂的概念,那就是生成对象的地方了. 本来直接就能创建的对象 ...

  4. Java设计模式系列-装饰器模式

    原创文章,转载请标注出处:<Java设计模式系列-装饰器模式> 一.概述 装饰器模式作用是针对目标方法进行增强,提供新的功能或者额外的功能. 不同于适配器模式和桥接模式,装饰器模式涉及的是 ...

  5. Java设计模式之【单例模式】

    Java设计模式之[单例模式] 何为单例 在应用的生存周期中,一个类的实例有且仅有一个 当在一些业务中需要规定某个类的实例有且仅有一个时,就可以用单例模式 比如spring容器默认初始化的实例就是单例 ...

  6. Java设计模式中的单例模式

    有时候在实际项目的开发中,我们会碰到这样一种情况,该类只允许存在一个实例化的对象,不允许存在一个以上的实例化对象,我们将这种情况称为Java设计模式中的单例模式.设计单例模式主要采用了Java的pri ...

  7. Java 设计模式系列(二三)访问者模式(Vistor)

    Java 设计模式系列(二三)访问者模式(Vistor) 访问者模式是对象的行为模式.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作.一旦这些操作需要修改的话,接受这个操作的数据结构则可以 ...

  8. Java 设计模式系列(十五)观察者模式(Observer)

    Java 设计模式系列(十五)观察者模式(Observer) Java 设计模式系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) Java ...

  9. Java 设计模式系列(十八)备忘录模式(Memento)

    Java 设计模式系列(十八)备忘录模式(Memento) 备忘录模式又叫做快照模式(Snapshot Pattern)或Token模式,是对象的行为模式.备忘录对象是一个用来存储另外一个对象内部状态 ...

随机推荐

  1. TagHighlight

    :UpdateTypesFile 命令会生成相应的上色高亮文件(生成的着色文件可以叫types_c.taghl)还有相应的tags文件,寻找tags或taghl_config.txt确定project ...

  2. [转载]深入理解JAVA的接口和抽象类

    深入理解Java的接口和抽象类 对于面向对象编程来说,抽象是它的一大特征之一.在Java中,可以通过两种形式来体现OOP的抽象:接口和抽象类.这两者有太多相似的地方,又有太多不同的地方.很多人在初学的 ...

  3. ios跳转

    目标应用程序:打开info.plist,添加一项URL types展开URL types,再展开Item1,将Item1下的URL identifier修改为URL Scheme展开URL Schem ...

  4. Android权限安全(3)权限的分级和自定义权限

    Android的不同权限分级 Normal 一般apk都可以用, Dangerous 一般apk都可以用,但有提示 SignatureOrSystem 特定的private key签名的或系统的apk ...

  5. TeeChart显示三维的图形,使用Surface

    绘制一个球 根据公式x^2+y^2+z^2=R^2; 令x=RsinAcosB  y=RcosAcosB z=RsinB using System; using System.Collections. ...

  6. C++ STL之迭代器注意事项

    1.两个迭代器组成的区间是前闭后开的 2.如果迭代器的有效性,如果迭代器所指向的元素已经被删除,那么迭代器会失效 http://blog.csdn.net/hsujouchen/article/det ...

  7. Catalan数推导(转载)

    Raney引理: 设整数序列A = {Ai, i=1, 2, …, N},且部分和Sk=A1+…+Ak,序列中所有的数字的和SN=1,在A的N个循环表示中,有且仅有一个序列B,满足B的任意部分和Si均 ...

  8. BZOJ2226: [Spoj 5971] LCMSum

    题解: 考虑枚举gcd,然后问题转化为求<=n且与n互质的数的和. 这是有公式的f[i]=phi[i]*i/2 然后卡一卡时就可以过了. 代码: #include<cstdio> # ...

  9. Java [Leetcode 70]Climbing Stairs

    题目描述: You are climbing a stair case. It takes n steps to reach to the top. Each time you can either ...

  10. SVN版本管理提示信息

    1. FAQ 1.路径或权限不足时将出现错误信息提示: http://192.134.4.251/svn/svnproject(路径不对)Error * PROPFIND request failed ...