Effective Item - 慎用延迟初始化

延迟初始化(lazy initialization),也就是在真正被使用的时候才开始初始化的技巧。

不论是静态还是实例,都可以进行延迟初始化。

其本质是初始化开销和访问开销之间的权衡。

毕竟是一种优化技巧,使用不当会起反效果。

尤其是在多线程场景中这种反效果会尤为明显,因为我们要对这个进行延迟初始化的field进行同步。

好,先一步步开始,如果初始化开销不值一提,我们只需要保证其不可变即可:

	private final FieldType field1 = computeFieldValue();

如果还有的商量,初始化开销可能让人在意,下面是最简单的的方式,直接在访问方法声明里加了synchoronized修饰,这种方式将访问开销最大化了:

	private FieldType field2;

	synchronized FieldType getField2() {
		if (field2 == null)
			field2 = computeFieldValue();
		return field2;
	}

        private static FieldType computeFieldValue() {
		return new FieldType();
	}

如果要改为静态的也不过是加上static修饰,但对于静态初始化,我们可以使用class holder方式:

	private static class FieldHolder {
		static final FieldType field = computeFieldValue();
	}

	static FieldType getField3() {
		return FieldHolder.field;
	}

	private static FieldType computeFieldValue() {
		return new FieldType();
	}

这种方式感觉不错,我们没有进行额外的同步处理,只有在访问getField3的时候FieldHolder才会被初始化。

所以这种情况属于没有增加访问开销也保证了延迟特性。

这次试试优化一下实例field的访问开销,最经典的就是double-check了,这个东西经常出现在笔试题中:

	private volatile FieldType field4;

	FieldType getField4() {
		FieldType result = field4;
		if (result == null) { 
			synchronized (this) {
				result = field4;
				if (result == null) 
					field4 = result = computeFieldValue();
			}
		}
		return result;
	}
	private static FieldType computeFieldValue() {
		return new FieldType();
	}

代码中使用了result局部变量,这样做虽然不是必要的,但这样可以确保field已被初始化的情况下被读取一次,可以提高少许效率。

以上就是延迟初始化的一些常用方式。

虽然看起来不错,但仍建议权衡访问和创建的开销,对于实例field使用double-check,对于静态field使用holder class,以在多线程访问时保证check-then-action的原子性。

时间: 2024-10-10 14:38:42

Effective Item - 慎用延迟初始化的相关文章

延迟初始化中的 双重检查模式 和 延迟占位类模式 你都用对了吗?

开篇: 正如<Effective Java>第二版中第71条目“慎用延迟初始化”所说: 大多数的域应该正常的被初始化而不是延迟初始化.如果为了达到性能目标,或者为了破坏有害的初始化循环,而必须延迟初始化一个域,就可以使用相应的延迟初始化方法. 对于实例域,就使用双重检查模式(double-check idiom):对于静态域,则使用 lazy initialization hodler class idiom. 对于可以接受重复初始化的实例域,也可以考虑使用单检查模式(single-check

Effective Item 13 - 慎用tagged class

其实作者的原标题是<Prefer class hierarchies to tagged classes>,即用类层次优于tagged class. 我不知道有没有tagged class这么一说,其实作者指的tagged class的是一个类描述了多种抽象,可以根据某个field决定不同的实例. 下面是书中例子,使用shape和部分表示长度的field构成形状并计算面积,脑补一下什么是tagged class: class Figure { enum Shape { RECTANGLE, C

Effective Java 之-----关于延迟初始化

1.大多数情况下,正常的初始化要优先于延迟初始化. private final FieldType field = computeFieldValue(); 2.如果利用延迟优化来破坏初始化的循环,就要使用同步访问方法,因为它是最简单.最清楚的替代方法. private FieldType field; synchronized FieldType getField(){ if(field == null){ field = computeFieldValue(); } return field

Effective Item 3 - 避免不必要的对象

通常,我们更喜欢重用一个对象而不是重新创建一个. 如果对象是不可变的,它就始终可以被重用. 下面是一个反面例子,Joshua Bloch明确指出[DON'T TO THIS]: String s = new String("stringette"); 该语句每次执行时都创建一个新的实例. String构造器中的参数"stringette"本身是一个实例,功能方面等同于那些通过构造器创建的对象. 如果这种语句放到循环里,效果会变得更糟. 于是我们只需要: String

Effective Item 6 - 覆盖equals方法时不要忘记hashCode方法

任何覆盖了equals方法的类都需要覆盖hashCode方法. 忽视这一条将导致类无法与基于散列的数据结构一起正常工作,比如和HashMap.HashSet和Hashtable. 下面是hashCode相关规范: ·在程序执行期间,只要对象的equals方法的比较操作所用到的信息没有被修改,那么对这个对象调用多少次hashCode,起结果必须始终如一地返回同一个证书. 如果是同一个程序执行多次,每次调用的结果可以不一致. ·如果两个对象根据equals方法比较是相等的,那么两个对象的hashCo

双重检查锁定与延迟初始化

在Java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化.此时程序员可能会采用延迟初始化.但要正确实现线程安全的延迟初始化需要一些技巧,否则很容易出现问题.比如,下面是非线程安全的延迟初始化对象的示例代码: public class UnsafeLazyInitialization { private static Instance instance; public static Instance getInstance() { if (instanc

Linux网卡延迟初始化错误解决方法

一.问题说明:        1.在系统中配置完网卡的配置后执行network services重启时提示"Device eth0 does not seem to be present,delaying initialization."设备不存在而延迟初始化的错误:service network restart Shutting down loopback insterface:                                                     

多线程场景下延迟初始化的策略

1.什么是延迟初始化 延迟初始化(lazy initialization,即懒加载)是延迟到需要域的值时才将它初始化的行为.如果永远不需要这个值,这个域就永远不会被初始化.这种方法既静态域,也适用于实例域. 最好建议“除非绝对必要,否则就不要这么做”. 2.延迟初始化线程安全的一个策略:同步 延迟初始化的一个好处,是当域只在类的实例部分被访问,并且初始化这个域的开销很高,那就可能值得进行延迟初始化. 但是在大多数情况下,正常的初始化要优先于延迟初始化.因为在多线程的场景下,采用某种形式的同步是很

Effective Item 17 - 关于方法的参数声明

给方法的参数加上限制是很常见的,比如参数代表索引时不能为负数.对于某个关键对象引用不能为null,否则会进行一些处理,比如抛出相应的异常信息. 对于这些参数限制,方法的提供者必须在文档中注明,并且在方法开头时检查参数,并在失败时提供明确的信息,即detect errors as soon as possible after they occur,这将成为准确定位错误的一大保障. 如果没有做到这一点,最好的情况是方法在处理过程中失败并抛出了莫名其妙的异常,错误的源头变得难以定位,但这是最好的情况.