如果要保证系统里一个类最多只能存在一个实例时,我们就需要使用单例模式。但是在多线程环境中,为了保证实例的唯一性其实并不简单。
一、简单实现
为了限制该类的对象被随意创建,需要保证该类的构造方法为私有的。另外,为了方便客户对象使用单例对象,我们需要提供一个全局访问点。
public class Singleton {
private static Singleton instance = new Singleton();
//other fields
private Singleton() {
}
public static Singleton getInstace() {
return instance;
}
//other methods
}
此实现是线程安全的,当多个线程同时去访问该类的getInstance方法,不会初始化多个不同的对象,这是因为JVM在加载此类时,对于static属性的初始化只能由一个线程执行一次。
二、延迟创建
有时候我们希望延迟实例化单例对象(static在加载类时就会被初始化),在第一次使用该类的实例的时候才去实例化这个单例对象。只要把单例对象的实例化放到getInstance方法中就可以了。
public class Singleton {
private static Singleton instance ;
//other fields
private Singleton() {
}
public static Singleton getInstace() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
//other methods
}
但是,这种实现是线程不安全的,如果两个进程同时进入if()语句的时候,两个线程会分别实例化不同的对象。所以需要给getInstance方法加上一个修饰词synchronized。但是对整个方法进行同步会导致性能的下降。我们只需要保证实例化对象的那段逻辑被一个线程执行就可以了。
public class Singleton {
private volatile static Singleton instance ;
//other fields
private Singleton() {
}
public static Singleton getInstace() {
if (instance == null) { //check if it is created.
synchronized (Singleton.class) {
//synchronized creation blocks.
if (instance == null) {
instance = new Singleton();
}
}
instance = new Singleton();
}
return instance;
}
//other methods
}
这种方法叫做double-checked locking模式。
还有一种实现单例延迟初始化的方法,叫做Initialization on demand holder
public class Singleton {
private Singleton() {
}
private static class LazyHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstace() {
return LazyHolder.instance;
}
//other methods
}