单例模式是指一个类只有一个实例,且该类能自行创建这个实例的一种模式。Spring中的Bean,数据库连接池,多线程连接池、Servlet都使用单例模式(Bean可以有多其他模式)。使用单例模式,有三个要点:私有化构造方法,定义静态实例、暴露实例方法。
单例模式主要有五种实现方法。
一、饿汉式
//饿汉式单例
public class Hungry {
private Hungry() {
}
private static Hungry hungry = new Hungry();
public static Hungry getInstance() {
return hungry;
}
}
这种方法在类加载时就初始化,造成内存的浪费。
二、懒汉式
/**
* 懒汉式单例
*/
public class LazyMan {
private LazyMan() {}
private static LazyMan lazyMan;
public static LazyMan getInstance() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
这样写是线程不安全的,如果线程A执行到lazyMan==null时,CPU切换到了线程B,B执行了new LazyMan(),CPU又切换回A,此时,A和B都实例化对象了。可以在getInstance()方法上加锁解决,但是会造成比较大的性能消耗。
三、DCL双重锁定
只在实例第一次创建的时候才同步,提高了性能。
public class DCLSingle {
private DCLSingle() {}
private volatile static DCLSingle dclSingle;
public static DCLSingle getInstance() {
if (dclSingle == null) {
synchronized (DCLSingle.class) {
if (dclSingle == null) {
/**
* 不是原子操作,因此要加上volatile关键字,禁止指令重排
* 分配内存空间、执行构造方法,初始化对象、把这个对象指向空间
*/
dclSingle = new DCLSingle();
}
}
}
return dclSingle;
}
}
四、静态内部类
调用getInstance()时,InnerClass才会加载,才会创建实例,保证了延时加载。线程安全由JVM保证。
//静态内部类
public class Holder {
private Holder() {}
public static Holder getInstance() {
return InnerClass.HOLDER;
}
public static class InnerClass {
private static final Holder HOLDER = new Holder();
}
}
五、枚举
//枚举
public enum EnumSingle {
INSTANCE;
public EnumSingle getInstance() {
return INSTANCE;
}
}