在之前的博文中曾经介绍过单例模式:
https://blog.csdn.net/timchen525/article/details/78244101
这里重新讨论们下单例模式,并且引入了一个基于枚举的单例模式。
分析如下代码:
**
* 懒汉模式
*/
public class SingletonExample {
// 私有构造函数
private SingletonExample() {
}
// 单例对象
private static SingletonExample instance = null;
// 静态的工厂方法
public static SingletonExample getInstance() {
if (null == instance) {
instance = new SingletonExample();
}
return instance;
}
}
上述代码也叫作懒汉模式,即只在第一次被调用的时候才初始化,
不是线程安全的。不安全的原因:当两个线程同时运行到if(null == instance)则,同时进行instance = new SingleExample();即被实例化两次。
改进添加同步操作(synchronized):
public class SingletonExample {
// 私有构造函数
private SingletonExample() {
}
// 单例对象
private static SingletonExample instance = null;
// 静态的工厂方法
public static synchronized SingletonExample getInstance() {
if (null == instance) {
instance = new SingletonExample();
}
return instance;
}
}
上述添加了synchronized使得该方法同一时刻只能被一个线程访问。因此,这种改进后的代码是线程安全的。
进一步基于synchronized进行改进:
public class SingletonExample {
// 私有构造函数
private SingletonExample() {
}
// 单例对象
private static SingletonExample instance = null;
// 静态的工厂方法
public static SingletonExample getInstance() {
if (null == instance) { // B
synchronized (SingletonExample.class) {
if (null == instance) { // 双重检测机制
instance = new SingletonExample(); // A-3
}
}
}
return instance;
}
}
分析:上述代码将同步synchronized移到判null之后,此时,可能有两个线程同时要去获取synchronized(SingletonExample.class),因此,在内部里面再次判断一次。这样写的好处,之后,null != instance可以直接return,而不用进行同步synchronized操作。
注意注意:上述
不是线程安全的。
instance = new SingletonExample()操作需要进行如下操作:
1)memory = allocate()分配对象的内存空间
2)ctorInstance()初始化对象
3)instance = memory设置instance的指向刚分配的内存
比如如下情况:
JVM和CPU优化,发生了指令重排
1)memory = allocate()分配对象的内存空间
3)instance = memory设置instance的指向刚分配的内存
2)ctorInstance()初始化对象
比如:两个线程,线程B执行到if(instance == null) 此时,线程B执行到instance=new SingletonExample();而new的操作被CPU指令重拍,比如到
3),此时,instance == null判断不成立,变返回instance产生错误。
再次改进上述线程不安全的单例(添加volatile)(
线程安全写法,推荐
):
volatile + 双重检测机制 = 实现线程安全的单例模式
public class SingletonExample {
// 私有构造函数
private SingletonExample() {
}
// 单例对象
private volatile static SingletonExample instance = null;
// 静态的工厂方法
public static SingletonExample getInstance() {
if (null == instance) {
synchronized (SingletonExample.class) {
if (null == instance) {
instance = new SingletonExample();
}
}
}
return instance;
}
}
改进成
饿汉模式(线程安全的)
:
即单例模式在类加载时进行创建:
public class SingletonExample {
// 私有构造函数
private SingletonExample() {
}
// 单例对象
private static SingletonExample instance = new SingletonExample();
// 静态的工厂方法
public static SingletonExample getInstance() {
return instance;
}
}
饿汉模式是
线程安全的,但是可能会引起程序初始化加载变慢,如果程序没有使用,则会造成资源浪费。
枚举模式实现线程安全(
最推荐,线程安全的
):
public class SingletonExample {
// 私有构造函数
private SingletonExample() {
}
public static SingletonExample getInstance() {
return Singleton.INSTANCE.getInstance();
}
private enum Singleton {
INSTANCE;
private SingletonExample singletonExample;
// JVM保证这个方法绝对只调用一次
Singleton() {
singletonExample = new SingletonExample();
}
public SingletonExample getInstance() {
return singletonExample;
}
}
}
JVM保证构造函数Single()被调用一次,因此是线程安全的。