【今日推荐】:为什么一到面试就懵逼!>>> Java并发编程——延迟初始化占位类模式-LMLPHP

——仅作笔记使用,内容多摘自《java并发编程实战》

在并发编程中,如果状态变量仅在单个线程中初始化和使用,自然是线程安全的,但一旦涉及到线程间的数据交互,如何声明一个用于多线程的单例状态变量才是安全的呢?最容易想到的,自然是通过一个工厂函数进行初始化并获取实例对象,如下:

public class Demo {

	private static Resource resource;

	public static Resource getInstance() {
		if(resource == null) {
			resource = new Resource();
		}
		return resource;
	}

}

然而上述的方法存在一个典型的竞态条件,在多线程的形况下getInstance可能会返回不同的对象,导致不可预知的错误。因此需要进行同步操作:

public class Demo {

	private static Resource resource;

	public synchronized static Resource getInstance() {
		if(resource == null) {
			resource = new Resource();
		}
		return resource;
	}

}

然而,众所周知的是,通过同步限制线程同时访问方法,会一定程度上影响程序的并发性能,于是产生了以下的初始化方法:

public class Demo {

	public static Resource resource= new Resource();

	public static Resource getInstance() {
		return resource;
	}

}

简言之,类中的静态变量在声明的时候就做初始化,可以经由JVM提供线程安全方便的保证,而无需自己添加synchronized关键字去进行同步,从而减少了线程同步带来的性能消耗。这种初始化方式被称为提前初始化。相对的,之前两种初始化方式,被称为惰性初始化或者延迟初始化

考虑到有些类的实例在初始化的时候,可能会产生比较高的开销,故人们希望在需要用到的时候再进行初始化,于是结合延迟初始化域JVM初始化静态域的特点,产生了较为常用的延迟初始化占位类模式

public class Demo {

	private static class ResourceHolder {
		public static Resource resource = new Resource();
	}

	public static synchronized Resource getInstance() {
		return ResourceHolder.resource;
	}

}

因为静态类在使用的时候才会被加载,故JVM第一次加载该静态类的时候,通过JVM即可实现静态域的线程同步,即满足了延迟加载的需求,也避开了同步带来的性能消耗。

04-14 02:05