单例模式是一种最简单的设计模式,它的类图上只有一个类!尽管从类设计的角度来说它简单,但是实现上还是会遇到问题。请系好安全带,发车了!
基本介绍
单例模式确保类中只有一个实例,并向外提供一个获取该实例的接口。它常常被用来管理共享资源,例如数据库的连接、线程池、缓存等。
单例模式一般有两种表现形式:
- 饿汉模式:类加载时,进行对象的实例化
- 懒汉模式:第一次引用类时,进行对象的实例化
饿汉模式
饿汉模式的创建步骤如下:
1. 创建一个私有的静态成员变量,并进行实例化;
1. 将构造函数进行私有化,让外界无法对它进行实例化;
1. 提供一个公开返回对象实例化的静态方法。
代码如下:
public class Singleton {
//创建一个私有的静态成员变量,并进行实例化
private static Singleton sInstance = new Singleton();
//将构造函数进行私有化
private Singleton{} {
}
//提供一个公开返回对象实例化的静态方法
public static Singleton getInstance() {
return sInstance;
}
}
懒汉模式
懒汉模式的创建步骤如下:
1. 声明一个私有的静态成员变量;
1. 将构造函数进行私有化,让外界无法对它进行实例化;
1. 提供一个公开的静态方法,判断成员变量是否为null,null则对成员变量进行实例化,不null则直接返回成员变量。
代码如下:
public class Singleton {
//声明一个私有的静态成员变量
private static Singleton sInstance;
//将构造函数进行私有化
private Singleton {
}
//提供一个公开的静态方法
public static Singleton genInstance() {
//对成员变量进行判空处理
if (sInstance == null) {
sInstance = new Singleton();
}
retrun sInstance;
}
}
比较
饿汉模式与懒汉模式主要的不同点是饿汉模式是线程安全的,懒汉模式线程不安全,假设现在有两个线程,线程a调用getInstance方法时,当成员变量sInstance为null,且没有进行实例化时,线程b调用了genInstance方法,此时在系统中就会存在两个sInstance的实例,这样就出现了线程不安全。
优化懒汉模式
其实只需要把getInstance()变为同步(sychronized)方法,这种在多线程下线程不安全的问题就可以轻易解决。代码如下:
public class Singleton {
//声明一个私有的静态成员变量
private static Singleton sInstance;
//将构造函数进行私有化
private Singleton {
}
//提供一个公开的静态方法
public static synchronized Singleton genInstance() {
//对成员变量进行判空处理
if (sInstance == null) {
sInstance = new Singleton();
}
retrun sInstance;
}
}
但是这样还是存在一种问题,其实只有在第一次执行此方法时,才需要同步,当成员变量sInstance实例化后,就不在需要同步这个方法了,之后每次调用这个方法,同步方法就成了累赘。
用“双重检查加锁”,减少同步的次数
利用双重检查加锁,首先检查是否实例已经创建了,如果未创建,才进行同步。这样一来,只有第一次会出现同步。代码如下:
public class Singleton {
//声明一个私有的静态成员变量
private static volatile Singleton sInstance;
//将构造函数进行私有化
private Singleton {
}
//提供一个公开的静态方法
public static Singleton genInstance() {
//双重检查加锁
if (sInstance == null) {
synchronized(Singleton.class) {
if (sInstance == null) {
sInstance = new Singleton();
}
}
}
retrun sInstance;
}
}
volatile关键字确保,当sInstance变量在进行实例化时,多个线程正确地处理sInstance变量。
定义
单例模式是确保一个类只有一个实例,并提供一个全局的访问入口。