天天看点

单例模式与double check

本文主要是讲double check,通常double check比较少用,一般是在高并发的情况下。但是建议大家写单例的时候都用上。

单例模式分两种:饿汉式与懒汉式,饿汉式是指在类加载时就进行实例化,而懒汉式是指在使用时才进行实例化。代码如下:

饿汉式:

public class Singleton {

private static Singleton s = new Singleton();

private Singleton() {

}

public static Singleton getInstance() {

return s;

}

}

懒汉式:

public class Singleton {

private static Singleton s = null;

private Singleton() {

}

public static Singleton getInstance() {

if (s == null) {

s = new Singleton();

}

return s;

}

}

double check在饿汉式中是肯定不会存在的,所以我们讨论的就是懒汉式的情况。看一种情况:如果同时两个线程同时访问getInstance()方法,线程A已经过了if判断,正在初始化对象,但是可能由于构造时间比较长,还没有构造完。这个时候s还没有指向的对象,而同时线程B在进行判断if的时候肯定也是null,自然也就可以通过,这时也可以进行new初始化,这样的话就会生成两个对象。因为是两个对象,两个线程中的数据肯定是不对的,所以会导致一系列的数据不一致的问题。这时我们可以考虑在方法上加synchronized关键字,或者在if外面加一层synchronized代码块,如下:

synchronized(Singleton.class) { // 注意这里不能是this,因为是静态对象,所以必须要用class

if (s == null) {

s = new Singleton();

}

}

这个时候我们发现并发问题是解决了,但是这时又会产生另外一个问题,如果s已经实例化了,就是说s != null了,而之后访问的对象依然要获取锁,这样的话,在高并发的环境下,性能就会非常差,因为每个请求都要上锁,必须挨个获取锁才行。此时我们可以在synchronized外面再加一层if(s == null)来判断,就可以解决这个问题了,如果初始化完成之后,再进行判断,则直接跳过,返回s的对象,完整的代码如下:

public static Singleton getInstance() {

if (s == null) {// 第一次判断是否初始化

synchronized (Singleton.class) {// 加锁是为了防止并发问题,只允许一个线程进入,并进行初始化。

if (s == null) {// 如果此时仍不为null,就进行初始化,

s = new Singleton();

}

}

}

return s;

}

double check就是指做了两次if判断,第一次的主要是作用是为了解决性能问题,第二次才是为了解决并发的问题,防止生成两个对象。