(四次元新浪微博源代码学习笔记4)实现单例模式

私有对象instance通过getInstance取出,保证instance只初始化一次。


一,多线程同步锁

Weiciyuan0.50的TimeLineBitmapDownloader代码采用了这种写法。

public class TimeLineBitmapDownloader {
    private static final Object lock = new Object();
    private static TimeLineBitmapDownloader instance;
    public static TimeLineBitmapDownloader getInstance() {
        synchronized (lock) {
            if (instance == null) {
                instance = new TimeLineBitmapDownloader(new Handler(Looper.getMainLooper()));
            }
        }
        return instance;
    }
}

如果两个线程同时想创建实例,其中一个线程加上锁之后,另一个线程就要等待。第一个线程创建完毕实例、释放锁之后,第二个线程加锁,发现实例已经创建,就不再创建了。

但是加锁属于耗时操作,应该尽量避免。

二,双重检查加锁(DCL,Double-Check Locking )

public class Singleton {
	private Singleton(){
	}
	private static volatile Singleton instance; // 注意volatile关键字


	public static Singleton getInstance() {
		Singleton tmp = instance; // 引入tmp局部变量的作用是为了在已初始化的情况下,仅仅访问volatile变量一次。
		if (tmp == null) {
			synchronized (Singleton.class) {
				tmp = instance;
				if (tmp == null) {
					instance = tmp = new Singleton();
				}
			}
		}
		return tmp;
	}
}

这种写法必须加上volatile,并且必须在java1.5以后才能成立。

不管哪个java版本,如果不加上volatile,线程A执行到new对象的时候,可能会出问题。这句包括三个过程:开辟一个内存空间;初始化数据;将instance指向分配的内存空间。java内存模型有一个out-of-order writes的机制,第二步和第三步的顺序是没有保证的。有可能实例域还没完全写入内存,instance就已经指向内存空间,这样instance不为null.如果这时切换到了线程B,线程B就直接返回了instance,但这个对象是没有构造完全的,导致错误。

volatile保证了可见性,对某个volatile字段的写操作happens-before后续对同一个volatile字段的读操作。java1.5之后很好地支持了volatile,解决了这个bug。


你可能感兴趣的:(android,单例模式,volatile)