单例模式的定义java
一个类有且仅有一个实例,而且自行实例化向整个系统提供。好比,多程序读取一个配置文件时,建议配置文件时,建议配置文件封装成对象。会方便操做其中的数据,又要保证多个程序读到的是同一个配置文件对象,就须要该配置文件对象在内存中是惟一的。安全
单例模式的做用多线程
简单说来,单例模式(也叫单件模式)的做用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(固然也能够不存在)。函数
单例模式的类图性能
如何保证对象的惟一性学习
思想:(1)不让其余程序建立该类对象;优化
(2)在本类中建立一个本类对象;网站
(3)对外提供方法,让其余程序获取这个对象;spa
步骤:(1)由于建立对象都须要构造函数初始化,只要将本类中的构造函数私有化,其余程序就没法再建立该类的对象;.net
(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的另外一个语义是保证变量修改的可见性。
好,到这里,就真正的把单例模式介绍完了,在此呢再总结一下单例类须要注意的几点:
1、单例模式是用来实如今整个程序中只有一个实例的。
2、单例类的构造函数必须为私有,同时单例类必须提供一个全局访问点。
3、单例模式在多线程下的同步问题和性能问题的解决。
4、懒汉式和饿汉式单例类。
本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/ysw-go/一、本博客的原创原创文章,都是本人平时学习所作的笔记,若有错误,欢迎指正。二、若有侵犯您的知识产权和版权问题,请通知本人,本人会即时作出处理文章。三、本博客的目的是知识交流所用,转载自其它博客或网站,做为本身的参考资料的,感谢这些文章的原创人员