概述
单例模式的作用
单件模式能够保证某一类型对象在系统中的唯一性,即某类在系统中只有一个实例。
解决方案
- 在类中创建一个类的静态变量(用于保存实例)
- 创建一个没有参数的构造函数,设置为private(让用户无法创建实例)
- 创建一个返回实例的方法(让类自身进行实例创建,用户调用这个方法来获取实例)
以下为这个类的类图:
实现单例模式的两种方式
实现单例模式主要有两种方式,一种是饿汉模式,一种懒汉模式。主要区别在于何时创建实例。
饿汉模式
public class Singleton{
// 1
private static Singleton instance = new Singleton;
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
饿汉模式就是在装载类的时候就创建对象实例(在代码1处)
懒汉模式
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
// 1
if(instance==null){
instance = new Singleton;
}
return instance;
}
}
懒汉模式则先不创建实例,等到需要使用对象实例时,才会对对对象实例进行创建
如何选择
- 如果单件模式实例在系统中经常会被用到,就选择饿汉模式。
- 如果单件模式在系统中会很少用到或者几乎不会用到,就选择懒汉模式。
扩展
单例模式下的线程安全问题
在多线程情况下,由于饿汉模式只创建一次对象,所以懒汉模式是线程不安全的。所以对懒汉模式需要进行一些修改。
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton;
}
}
}
return instance;
}
只在实例未被创建的时候枷锁处理。同时也能保证多线程的安全。这种做法被称为Double-Check Locking(双重锁定)。
双重锁定是为了防止以下情况的发生:同时有两个线程调用getInstance()方法是,两个线程同事通过第一层判断。由于lock机制,一个线程进入创建一个实例后并退出,而另一个线程再次进入创建实例并退出。这样就会导致创建了两次实例。
多例模式
多例模式是单例模式一种扩展,可以产生固定数量的对象
public class Singleton{
// 表示当前需要调用对象的序号
private static int num = 1;
// 确认产生对象的数量
private static int maxNum = 3;
private static ArragryList<Singleton> instanceList;
// 使用static初始化instanceList
static{
for(int i=0;i<maxNum;i++){
instanceList.add(new Singleton())
}
}
private Singleton(){}
public static Singleton getInstance(){
Singleton s = instanceList.get(num);
num++;
// 如果实例的序号已经大于数目,则从1开始获取
if(num>maxNum){
num=1;
}
return s;
}
}