首先来明确一个问题,那就是在某些情况下,有些对象,我们只需要一个就可以了,比如,一台计算机上可以连好几个打印机,但是这个计算机上的打印程序只能有一个,这里就可以通过单例模式来避免两个打印作业同时输出到打印机中,即在整个的打印过程中我只有一个打印程序的实例。
一、基本定义
通过上面简单介绍,我们可以对单例模式有一个简单的认识。所谓单例模式就是确保某一个类只有一个实例,并
且提供一个全局访问点。
从上面可以看出单例模式有如下几个特点:
一、它只有一个实例。
二、它必须要自行实例化。
三、它必须自行想整个系统提供访问点。
简单说来,单例模式(也叫单件模式)的作用就是保证在整个应用程序的生命周期中,
任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。
二、实现
public class SingletonClass {
private static SingletonClass instance = null;
public static SingletonClass getInstance() {
if(instance == null) {
instance = new SingletonClass();
}
return instance;
}
private SingletonClass() {
}
}
同步:
线程A希望使用SingletonClass,调用getInstance()方法。因为是第一次调用,A就发现instance是null的,于是它开始创建实例,就在这个时候,CPU发生时间片切换,线程B开始执行,它要使用SingletonClass,调用getInstance()方法,同样检测到instance是null——注意,这是在A检测完之后切换的,也就是说A并没有来得及创建对象——因此B开始创建。B创建完成后,切换到A继续执行,因为它已经检测完了,所以A不会再检测一遍,它会直接创建对象。这样,线程A和B各自拥有一个SingletonClass的对象——单例失败
public class SingletonClass {
private static class SingletonClassInstance {
private static final SingletonClass instance = new SingletonClass();
}
public static SingletonClass getInstance() {
return SingletonClassInstance.instance;
}
private SingletonClass() {
}
}
三、多线程问题解决方案
这里有三种解决方案:
第一、 使用synchronized来处理。也就是说将getInstance()方法变成同步方法即可。
public class Singleton {
//利用静态变量来记录Singleton的唯一实例
private static Singleton uniqueInstance;
/*
* 构造器私有化,只有Singleton类内才可以调用构造器
*/
private Singleton(){
}
// 同步 getInstance 方法
public static synchronized Singleton getInstance(){
if(uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
第二、 懒加载 直接初始化静态变量。这样就保证了线程安全
public class Singleton {
/*
* 利用静态变量来记录Singleton的唯一实例
* 直接初始化静态变量,这样就可以确保线程安全了
*/
private static Singleton uniqueInstance = new Singleton();
/*
* 构造器私有化,只有Singleton类内才可以调用构造器
*/
private Singleton(){
}
public static Singleton getInstance(){
return uniqueInstance;
}
}
第三、 用“双重检查加锁”,在getInstance()中减少使用同步
public class Singleton {
/*
* 利用静态变量来记录Singleton的唯一实例
* volatile 关键字确保:当uniqueInstance变量被初始化成Singleton实例时,
* 多个线程正确地处理uniqueInstance变量
*
*/
private volatile static Singleton uniqueInstance;
/*
* 构造器私有化,只有Singleton类内才可以调用构造器
*/
private Singleton(){
}
/*
*
* 检查实例,如果不存在,就进入同步区域
*/
public static Singleton getInstance(){
if(uniqueInstance == null){
synchronized(Singleton.class){ //进入同步区域
if(uniqueInstance == null){ //在检查一次,如果为null,则创建
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
很显然第三种是最好的,一,如果不使用不会占用内存,二 效率也比较高;
四、模式优缺点
优点
一、节约了系统资源。由于系统中只存在一个实例对象,对与一些需要频繁创建和销毁对象的系统而言,单
例模式无疑节约了系统资源和提高了系统的性能。
二、因为单例类封装了它的唯一实例,所以它可以严格控制客户怎样以及何时访问它。
缺点
一、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
二、单例类的职责过重,在一定程度上违背了“单一职责原则”。
五、模式使用场景
下列几种情况可以使用单例模式。
一、系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。
二、客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
六、总结
1. 单例模式中确保程序中一个类最多只有一个实例。
2. 单例模式的构造器是私有了,而且它必须要提供实例的全局访问点。
3. 单例模式可能会因为多线程的问题而带来安全隐患。
一、由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。
二、单例类的职责过重,在一定程度上违背了“单一职责原则”。