单例模式
单例模式,顾名思义,用来保证一个对象只能创建一个实例,是最常用的设计模式,它具有易于理解、使用简便等特点,但是用不好的话也会造成弊大于利的后果。
1. 普通单例模式
最普通的单例模式,但只适合单线程编程中。
/**
* 最普通的单例模式
*/
public class SimpleSingleton {
private static SimpleSingleton instance;
public SimpleSingleton() {
}
public static SimpleSingleton getInstance() {
if(instance == null)
instance = new SimpleSingleton();
return instance;
}
//...
public static void main(String[] args) {
SimpleSingleton.getInstance();
}
}
2. 拥有双重校验锁机制的同步锁单例模式
存在这样一种情况,一个线程首先使用新构造器实例化单例对象,同时又有一个线程获取单例时判断单例实例是否为空,此时第一个线程还没有完成实例化操作,所以第二个线程会发现这个实例为空的,并且也开始实例化单例对象。
这种情况看似发生的概率很小,但是当创建单例对象需要较长时间的情况下,这个情况发生的概率就会变高。
/**
* 拥有双重校验锁机制的同步锁单例模式
*/
public class DoubleLockSingleton {
private static DoubleLockSingleton instance;
public DoubleLockSingleton() {
}
public static DoubleLockSingleton getInstance() {
if(instance == null){
synchronized (DoubleLockSingleton.class){
if(instance == null)
instance = new DoubleLockSingleton();
}
}
return instance;
}
//...
public static void main(String[] args) {
DoubleLockSingleton.getInstance();
}
}
3. 无锁的线程安全单例模式
利用类加载的性质,直接在声明时实例化静态成员来保证一个类只有一个实例。这种方式避免了使用同步锁机制和判断实例是否被创建的额外检查。
/**
* 无锁的线程安全单例模式
*/
public class LockFreeSingleton {
private static final LockFreeSingleton instance = new LockFreeSingleton();
public LockFreeSingleton() {
}
public static LockFreeSingleton getInstance() {
return instance;
}
//...
public static void main(String[] args) {
LockFreeSingleton.getInstance();
}
}
关于提前加载和延迟加载
按照实例对象被创建的时机,可以将单例模式分为两类。
- 提前加载单例模式:应用开始时创建单例实例。
- 延迟加载单例模式:在getInstance方法首次被调用时才调用单例构造器创建实例。
上面说到的无锁的线程安全单例模式在早期版本的java中属于提前加载单例模式,但是后来版本中,类只有在使用时才会被加载,这是就成为了延迟加载单例模式。
这主要取决于JVM虚拟机的实现机制,所以在设计时要避免与JVM的实现机制进行绑定。
在虚拟机执行参数中增加-XX:+TraceClassLoading
参数,可以监控类的加载。