在某些情况下,我们需要让某个类在全局只有一个实例(比如用于资源管理或者存储全局配置信息的类),这种情况下,如果我们不限制该类的创建,那么任何时候都可以使用new来创建对象。因此,我们要让该类自己创建对象并为系统提供方法去获得该唯一的对象,这种模式成为单例模式。
单例模式有许多种形式,下面列出几种常见的:
一、饿汉模式
饿汉中的“饿”表示即使我还没使用该对象,我也创建好,等到需要的时候就返回这个对象。
/**
* 饿汉模式
* 先初始化单例变量,当使用的时候返回该变量
*/
class Singleton
{
//开始便初始化Singleton对象
private static Singleton singleton = new Singleton();
//私有的构造方法,使外界不能生成该对象
private Singleton()
{
//初始化代码
}
//静态方法,调用是返回该唯一的Singleton对象
public static Singleton getSingleton()
{
return singleton;
}
}
这种形式的单例问题在于当我们引入了该类但是又没有使用的时候,类中的单例变量就浪费了内存空间。
二、懒汉模式
相对于饿汉,懒汉在开始并不初始化单例变量,而是等到第一次使用的时候才去创建对象。
/**
* 懒汉模式
* 当使用的时候才初始化单例变量
*/
class Singleton
{
//不初始化该变量
private static Singleton singleton = null;
//私有的构造方法,使外界不能生成该对象
private Singleton()
{
//初始化代码
}
//静态方法,如果没有创建单例变量则创建并返回
public Singleton getSingleton()
{
if(singleton == null)
{
singleton = new Singleton();
}
return singleton;
}
}
懒汉没有了饿汉的空间问题,但是,在多线程的情况下,当多个线程以及其接近的顺序逼近if(singleton == null),就有可能同时判断成功而去创建对象,因此该形式是类型不安全的。
三、改进的懒汉模式
使用synchronized关键字能防止多个对象同时调用getSingleton()方法,解决同步问题:
/**
* 改进的懒汉模式
* 解决多线程的同步问题
*/
class Singleton
{
//不初始化该变量
private static Singleton singleton = null;
//私有的构造方法,使外界不能生成该对象
private Singleton()
{
//初始化代码
}
//静态方法,如果没有创建单例变量则创建并返回
//同时是同步方法,同一时间只能一个对象访问
public synchronized Singleton getSingleton()
{
if(singleton == null)
{
singleton = new Singleton();
}
return singleton;
}
}
这种情况防止了多个对象的创建,但是,我们需要同步的只是对对象的创建,而对象的返回是安全的,否则同意时间只能有一个外部对象能获得该单例的实例,影响了性能。
四、双重检查
为了改进上述的性能问题,现在只对对象的创建进行同步:
/**
* 只对对象创建执行同步
* 这种情况依然会产生同步问题
*/
class Singleton
{
//不初始化该变量
private static Singleton singleton = null;
//私有的构造方法,使外界不能生成该对象
private Singleton()
{
//初始化代码
}
//静态方法,如果没有创建单例变量则创建并返回
public Singleton getSingleton()
{
if(singleton == null)
{
//只同步对象的创建
synchronized(Singleton.class)
{
singleton = new Singleton();
}
}
//对象的返回不需同步
return singleton;
}
}
这种形式依然会产生懒汉的线程同步问题,当多个线程同时通过if(singleton == null)的判断时,它们还是会依次创建单例实例,因此应该执行双重检查。
/**
* 执行双重检查
* 解决同步问题和性能问题
*/
class Singleton
{
//不初始化该变量
private static Singleton singleton = null;
//私有的构造方法,使外界不能生成该对象
private Singleton()
{
//初始化代码
}
//静态方法,如果没有创建单例变量则创建并返回
public Singleton getSingleton()
{
//第一重检查
if(singleton == null)
{
//同步对象的创建
synchronized(Singleton.class)
{
//第二重检查
if(singleton == null)
singleton = new Singleton();
}
}
//对象的返回不需同步
return singleton;
}
}
五、内部类形式
此外,使用内部类也可以解决以上问题:
/**
* 使用内部类的形式,没有同步和性能问题
*/
class Singleton
{
//持有单例实例的内部类,但是不会立即初始化
private static class SingletonContainer
{
//外部类可以访问内部类的私有成员
private static Singleton singleton = new Singleton();
}
private Singleton()
{
//初始化代码
}
//静态方法,如果没有创建单例变量则创建并返回
public Singleton getSingleton()
{
return SingletonContainer.singleton;
}
}