单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例(百度百科)。
单例的实现有以下几种方式:
- 非延迟加载,也称饿汉模式,顾名思义类加载后就实例化对象。
public class Singleton {
//私有构造方法
private Singleton() {}
//Final 实例
private static final Singleton instance = new Singleton();
//静态无参get方法
public static Singleton getInstance() {
return instance;
}
}
- 延迟加载,也称懒汉模式,等到用到该实例时再加载。
public class Singleton {
//私有构造方法
private Singleton() {}
//Final 实例
private static Singleton instance =null;
//静态无参get方法
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
synchronized
是必须要加的,否则多线程下会出问题:
线程1先进入
getInstance
方法,然后在new Singleton()
执行之前,线程2也进入getInstance
方法,因为线程1还没有执行new Singleton()
操作,固线程2的if(instance == null)
判断也是成立的,因此线程2也new
了一个Singleton对象,这样单例模式就失效了,因此需要加同步锁。
- 双检查模式
public class Singleton {
//私有构造方法
private Singleton() {}
//声明
private static volatile Singleton instance =null;
//静态无参get方法
public static Singleton getInstance() {
if(instance == null) {//1
synchronized (Singleton.class) {
if(instance == null) {//2
instance = new Singleton();//3
}
}
}
return instance;
}
}
Singleton
一定要加volatile
修饰,否则同样在多线程下出问题:
线程1先进入
getInstance()
方法,执行到//3处时,线程2进入getInstance()
,此时线程1并未初始化完全instance
对象(instance = new Singleton()
并非是原子操作,第一步为instance
分配内存,此时instance
已不再为null
,第二步调用其构造方法实例化instance
),然而线程2在//1处判断到instance
不为空,则直接返回了未初始化完全的instance
对象,故而导致系统崩溃。
- 使用ThreadLocal修复双重检测
public class Singleton {
private static final ThreadLocal<Singleton> threadLocalSingleton = new ThreadLocal<Singleton>();
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
// 每个线程第一次都会是null
if (threadLocalSingleton.get() == null) {
createInstance();
}
return singleton;
}
private static final void createInstance() {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
//标记当前线程已初始化过singleton对象
threadLocalSingleton.set(singleton);
}
}
- 内部类模式
参考: 单例模式与双重检测
更多关于volatile
的知识,请看另一篇文章Java基础之volatile