首页 > 代码库 > java常用设计模式-单例模式
java常用设计模式-单例模式
java设计模式系列之单例模式
单例模式定义
一个类有且仅有一个实例,并且自行实例化向整个系统提供。
单例模式作用
简单来说,就是在整个程序运行的生命期中,任何一个时刻,单例类的实例都只有一个(当然也可以一个都没有)。
如何保证对象的唯一性
思想:(1)不让其他程序创建该类对象;
(2)在本类中创建该类对象;
(3)创建一个对外提供的方法,可以让其他类进行调用。
步骤:(1)因为创建对象的时候都要初始化构造函数,将该类的构造函数私有化,其他程序就无法再创建该类对象;
(2)就是在本类中创建本类对象;
(3)定义一个方法,返回该类对象,让其他类可以调用此方法(作用:可控,对象的由自己决定,不能谁想new就new)。
我们直接在类中自己创建一个对象,getSingle方法只负责把对象返回给调用者,实现单例可控(你能获取我的方法,但能拿到的是我自己创建的对象)
代码体现方式:
(1)私有化构造函数 (2)创建私有并静态的本类的对象 (3)创建公有的静态的调用方法,返回该对象
代码实现主要有两种方式:饿汉模式和饱汉模式
饿汉模式:类加载的时候对象就已经存在了内存中,是线程安全的,在类创建好的同时竟已经创建好了一个静态的对象供系统使用,以后不再改变。(为什么是线程安全的,后面给出解释)
懒汉模式:类加载的时候对象还不存在,就是所谓的延迟加载方式,需要时再创建对象,此模式如果在创建对象时不加上synchronized则会导致对对象的访问不是线程安全的(为啥不是线程安全的,后面解释)
下面解释一下,懒汉式的线程不安全性,通常情况下,我们建议写饿汉式,因为它是线程安全的
当线程访问懒汉式时,懒汉式的方法会对共性数据进行多条语句的操作
两个线程,线程1和线程2,当线程1执行到sin为null,还没创建对象时,线程2也执行到sin为null并且创建了对象,此时,两个线程就会创建两个对象,违背了单例模式的原则。
出现线程安全的问题,为了解决这种问题,加入同步机制(不熟悉同步机制请百度):静态同步函数的锁是类的字节码文件对象
这样一种设计可以保证只产生一个实例,并且只会在初始化的时候加同步锁,看似精妙绝伦,但却会引发另一个问题,这个问题由指令重排序引起。(这一部分来自: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变量禁止指令重排序:
代码实现:
本博客的转载之处:http://www.cnblogs.com/ysw-go/
java常用设计模式-单例模式