保证一个类仅有一个实例,并向整个系统提供唯一访问其 实例对象 的方式。主要用于解决一个全局使用的类被频繁地创建和销毁造成资源的浪费问题。
应用场景:网站计数器,数据库连接池,线程池等
1.饿汉模式单例实现:
public class Singleton {
private Singleton(){}
private static Singleton singleton = new Singleton();
public static Singleton getInstance(){
return singleton;
}
}
优点:不存在线程安全问题。 缺点:每次调用都实例化,占用空间 。
2.懒汉模式单例实现(演进分析)
懒汉模式单例实现①
public class Singleton {
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getInstance() {
//创建实例前判断实例对象是否为空,防止重复创建对象
if(singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
该实现方式在单线程环境下无问题,但在高并发的情况下,singleton实例未初始化时,容易存在两个或多个线程判断singleton为空,多个线程同时实例化singleton的情况。
懒汉模式单例实现②——synchronized方法保证同步
public class Singleton {
private static Singleton singleton;
private Singleton(){}
//加入synchronized关键字
public static synchronized Singleton getInstance(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
该实现虽然简单地解决了多线程竞争的问题,但是如果程序对getInstance方法调用频率很高的话,synchronized锁住整个方法,加之,后续多次进行singleton==null的判断毫无必要,且该非空判断又在synchronize方法内,程序的性能将会大打折扣。
懒汉模式单例实现③——DCL(Double Check Lock,双重检查锁)
public class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
/**
*先判断singleton是否实例化,若已实例化则返回singleton实例,
*若无实例化则在同步方法内实例化singleton
*/
if (singleton != null){
return singleton;
}
synchronized(Singleton.class){
/**
*该if语句防止在线程A实例化singleton时,有另外的线程B执行最外层的if语句,
*线程A实例化完成singleton后,线程B又进入同步方法实例化singleton的情况
*/
if(singleton == null){
singleton = new Singleton();
}
}
}
}
该实现很好地解决了 懒汉单例模式② 中存在的问题,但是singleton = new Singleton()中,由于JVM指令重排序,无法保证该语句是先引用singleton指向堆内存空间(新对象分配的空间),还是先实例化对象(堆内存装配)。问题在于,若是在A线程中,JVM先实例化singleton对象,后引用singleton指向堆内存空间,该情景下当线程A实例化singleton对象(new singleton())时,引用变量singleton仍然指向null,若线程B调用getInstance时判断singleton为null,则返回singleton实例,则引发异常。
懒汉模式单例实现④——volatile关键字配合DCL
public class Singleton {
//volatile关键字
private volatile static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if (singleton != null){
return singleton;
}
synchronized(Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
}
volatile关键字保证singleton的可见性以及防止重排序,并且volatile不会造成线程阻塞的问题。
3.Lazy initialization holder class模式
public class Singleton {
/**
*类级内部类,只有第一次被使用,才会被创建对象,因此在类初始化时不会初始化对象
*实现了延迟加载
*/
private static class SingletonHolder {
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
JVM隐式执行同步的情况: