利用Stream类型的“懒”操作
代码中的很多操作都是Eager的,比如在发生方法调用的时候,参数会立即被求值。总体而言,使用Eager方式让编码本身更加简单,然而使用Lazy的方式通常而言,即意味着更好的效率。
本篇文章就是为了展示Java 8中新特性是如何让我们能够更方便的写出Lazy方式代码。
延迟初始化
对于会消耗较多资源的对象,使用延迟初始化是比较好的选择。这不仅能够节省一些资源,同时也能够加快对象的创建速度,从而从整体上提升性能。
但是对一个对象实现延迟初始化时,需要注意的一点就是这些实现细节不应该暴露给用户,即用户能够按照正常的流程来使用该对象。
典型实现
public class Heavy {
public Heavy() {
System.out.println("Heavy created"); }
public String toString() {
return "quite heavy"; }
}
public class HolderNaive {
private Heavy heavy;
public HolderNaive() {
System.out.println("Holder created");
}
public Heavy getHeavy() {
if(heavy == null) {
heavy = new Heavy();
}
return heavy;
}
//...
}
利用以上的代码:
final HolderNaive holder = new HolderNaive();
System.out.println("deferring heavy creation...");
System.out.println(holder.getHeavy());
System.out.println(holder.getHeavy());
// Holder created
// deferring heavy creation...
// Heavy created
// quite heavy
// quite heavy
上述代码在单线程环境中能够正常工作,但是在多线程环境中就不尽然了。当多个线程同时调用getHeavy方法时,也许会发生竞态条件(Race Condition),导致有多个Heavy实例被创建,最直观的解决方案就是给该方法加上synchronized关键字:
public synchronized Heavy getHeavy() {
if(heavy == null) {
heavy = new Heavy();
}
return heavy;
}
这样虽然能够保证确实只有一个heavy实例被创建,但是弊端也很明显:每次调用getHeavy方法时,都需要进入代价高昂的synchronized代码区域。实际上,只有在第一次需要创建Heavy实例的时候,才需要保证线程安全。当该实例创建完毕之后,再使用synchronized来保证线程安全就没有必要了。
使用Lambda表达式
这里我们需要用到的是函数接口Supplier,其中定义了一个get方法用来得到需要的实例:
Supplier<Heavy> supplier = () -> new Heavy();
Supplier<Heavy> supplier = Heavy::new;
除了利用Lambda表达式来