单例模式
单例定义
一个类有且仅有一个实例,并且自行实例化向整个系统提供。
类结构图
单例特点
单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
(1)单例类只能有一个实例。
(2)单例类必须自己创建自己的唯一的实例。
(3)单例类必须给其他所有对象提供这一实例。
虽然单例模式中的单例类被限定只能有一个实例,但是单例模式和单例类可以很容易被推广到任意且有有限个实例的情况,这时称多例模式和多例类。
单例模式本质:控制实例数目。
实现方式
1.第一种方案 懒汉式
/**
*
* 项目名称:设计模式
* 类名称:Singleton
* 类描述: 懒汉式单例模式
* 创建人:YinXiangBing
* 创建时间:2014-7-2 下午06:22:52
* @version 1.0
*
*/
public class Singleton{
//4:定义一个变量来存储创建好的类实例
//5:因为这个变量要在静态方法中使用,所以需要加上static修饰
private static Singleton instance = null;
//1:私有化构造方法,好在内部控制创建实例的数目 ?
private Singleton(){
}
//2:定义一个方法来为客户端提供类实例 ??
//3:这个方法需要定义成类方法,也就是要加static ??
public static Singleton getInstance(){
//6:判断存储实例的变量是否有值 ?
if(instance == null){
//6.1:如果没有,就创建一个类实例,并把值赋值给存储类实例的变量
instance = new Singleton();
}
//6.2:如果有值,那就直接使用
return instance;
}
}
虽然是安全的,但是效率非常低在一个时候只有一个线程能访问,同时返回一个对象
/**
*
* 项目名称:设计模式
* 类名称:Singleton
* 类描述:懒汉式单例模式
* 创建人:YinXiangBing
* 创建时间:2014-7-3 上午11:04:25
* @version 1.0
*
*/
public class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
线程安全并且效率高能有多个线程访问 (双重检查加锁机制)
/**
*
* 项目名称:设计模式
* 类名称:Singleton
* 类描述:懒汉式单例模式
* 创建人:YinXiangBing
* 创建时间:2014-7-3 上午11:09:20
* @version 1.0
*
*/
public class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static synchronized Singleton getInstance(){
if(instance == null){
synchronized (Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
从中我们可以看出,后者双重检查加锁机制的单例模式,可以减少系统同步时的资源消耗,提高了执行效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了。
2.第二种方案 饿汉式
/**
*
* 项目名称:设计模式
* 类名称:Singleton
* 类描述:饿汉式单例模式
* 创建人:YinXiangBing
* 创建时间:2014-7-2 下午06:16:09
* @version 1.0
*
*/
public class Singleton{
//4:定义一个静态变量来存储创建好的类实例
//直接在这里创建类实例,只会创建一次
private static Singleton instance = new Singleton();
//1:私有化构造方法,好在内部控制创建实例的数目 ?
private Singleton(){
}
//2:定义一个方法来为客户端提供类实例 ??
//3:这个方法需要定义成类方法,也就是要加static ??
//这个方法里面就不需要控制代码了 ??
public static Singleton getInstance(){
//5:直接使用已经创建好的实例
return instance;
}
}
实现本质
懒汉式是典型的时间换空间:每次获取实例都会进行判断,看是否需要创建实例,占用判断的时间;如果一直没有人使用的话,那就不会创建实例,从而节约内存空间。这里体现的是延迟加载和缓存的思想。(Lazy Load:开始不需要加载资源或者数据,一直等到就要使用这个资源或数据时,才会加载。Cache:数据先缓存到内存里,每次操作数据时,先到内存里面查找需要的数据,如果缓存里有,那么就直接使用,如果没有那么就获取它并设置到缓存中,下一次访问就可以直接从缓存中得到。这两种思想,在很多框架技术下都有体现,比如Spring中的依赖注入,虚拟机类的加载机制,临时表的一些技术等)。
饿汉式是典型的空间换时间:当类加载的时候才会去创建类实例,不管你用不用这个类,都会先创建出来;每当程序调用时,就不需要再花时间判断,从而节省运行时间。
单例优点
1.实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一的一个实例。
2.灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
3.跨平台
使用合适的中间件,可以把singleton模式扩展为跨多个JVM和多个计算机工作。
4.任何类易实现
把一个类的构造函数变成私有并且在其中增加相应的静态函数和变量,就把这个类变为singleton。
单例缺点
1.开销效率
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
2.不能继承
从singleton派生的类并不是singleton。如果要使其成为singleton,必须要增加所需的静态函数和变量。
3.不透明性
singleton的使用者知道它们正在使用一个singleton ,因为它们必须要调用instance方法。
实践应用
private static TaskManager manager = new TaskManager();
private TaskManager(){//此类不能被实例化,且不可有子类
}
//单例返回 manager
public static TaskManager getInstance(){
return manager;
}
可以看出,我们使用了饿汉式的单例模式.
平常用到单例的应用也不少,比如:
每台计算机可以由若干个打印机,但只能有一个Printer Spooler,避免有两个作业同时输出到打印机。
一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键。否则会出现主键重复。
我们的平台数据库主键发号和单据的自动发号就是利用了单利模式。