gpt4 book ai didi

java - 考虑到 sychronized 关键字的成本,有哪些技巧可以使延迟初始化线程安全且高效?

转载 作者:行者123 更新时间:2023-12-01 16:28:24 24 4
gpt4 key购买 nike

在阅读了 Venkat Subramaniam 所著的《Java 函数式编程》第 106-108 页中的“昂贵资源的延迟初始化”之后,发现很难理解此代码片段的技巧

我的理解:变量heavy上课Holder类型为Supplier<Heavy>

对比

本地类HeavyFactory方法createAndCacheHeavy里面是一个子类extendsSupplier

似乎只运行一次来​​执行该 lambda 方法,然后更改 Holder.heavy 类的外部成员变量

我对下面的代码感到困惑,然后重载被分配了指向子类扩展供应商的新引用

如果有人可以在这里分享提示技巧,以获得作者建议的优点,以节省同步关键字的性能损失,并注意线程安全。它还提到了虚拟代理模式。我是否错过了任何理解它的关键信息?

package fpij;

import java.util.function.Supplier;

public class Holder {
//only run once here? before heavy get reassigned to HeavyFactory, the local class to that lambda method?
private Supplier<Heavy> heavy = () -> createAndCacheHeavy();

public Holder() {
System.out.println("Holder created");
}

public Heavy getHeavy() {
//the 2nd time it will call always the HeavyFactory.heavyInstance?
return heavy.get();
}


private synchronized Heavy createAndCacheHeavy() {
//create a local class inside method? Is the real trick/hack here I missed out so it will avoid 2nd time the synchronized penalty?
class HeavyFactory implements Supplier<Heavy> {
private final Heavy heavyInstance = new Heavy();

public Heavy get() { return heavyInstance; }
}

if(!HeavyFactory.class.isInstance(heavy)) {
heavy = new HeavyFactory();
}

return heavy.get();
}

public static void main(final String[] args) {
final Holder holder = new Holder();
System.out.println("deferring heavy creation...");
System.out.println(holder.getHeavy());
System.out.println(holder.getHeavy());
}
}


package fpij;

public class Heavy {
public Heavy() { System.out.println("Heavy created"); }

public String toString() { return "quite heavy"; }
}

最佳答案

如果您确实关心同步的成本,有一种简单的方法可以使其正常工作,同时保持初始化惰性。

它利用类加载器确保类加载时同步的特性。它保证在类完全加载之前没有其他线程能够访问该类。而且类加载实际上是惰性的:只有在第一次实际使用该类时才加载该类。

如果类 HeavyFactory 的唯一功能是提供单例实例,则只有在调用 getInstance 时才会加载该实例,并且一切都会很好地运行。

class HeavyFactory {

private static final Heavy heavyInstance = initInstance();

public static Heavy getHeavyInstance() {
return heavyInstance;
}

private static Heavy initInstance() {
heavyInstance = new HeavyInstance();
[...] // Other init stuff
return heavyInstance;
}
}

编辑:复杂对象的初始化和依赖关系的连接非常常见,以至于像 JEE 或 Spring 这样的框架已经实现了简化它的方法。

例如,如果您使用 spring,您将能够将给定服务声明为单例,然后在需要时声明对其的依赖关系。当 spring 框架按照正确的顺序初始化时,init 就会完成:

// We instruct spring to create a shared instance of heavy
// with @Component annotation
// The instance would be created eagerly at the start of the app.
@Component
public class Heavy {
public Heavy() { System.out.println("Heavy created"); }

public String toString() { return "quite heavy"; }
}



// For the example another service using the Heavy shared instance
@Component
public class ClientDependingOnHeavy {

// We ask spring to fill it automatically the shared instance for us.
@Autowired
private Heavy heavy;


public String foo() {
//So we can use the instance like any object.
System.out.println(heavy.toString());
}
}

Spring 或 JEE 是相当复杂的高级框架。对于单个案例来说,它们根本不值得。对于整个应用程序来说,这是有意义的。如果您还不了解第二个示例,则需要大量阅读/教程才能使它们发挥作用。但从长远来看,这可能是值得的。

关于java - 考虑到 sychronized 关键字的成本,有哪些技巧可以使延迟初始化线程安全且高效?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62114335/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com