我有一个类,其实例被初始化并由底层平台使用。
class MyAttributeConverter implements AttributeConverter<XX, YY> {
public YY convertToDatabaseColumn(XX attribute) { return null; }
public XX convertToEntityAttribute(YY dbData) { return null; }
}没有什么问题,我想我需要添加一些静态方法作为方法引用。
private static MyAttributeConverter instance;
// just a lazy-initialization;
// no synchronization is required;
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}
// do as MyAttributeConverter::toDatabaseColumn(xx)
public static YY toDatabaseColumn(XX attribute) {
return instance().convertToDatabaseColumn(attribute);
}
public static XX toEntityAttribute(YY dbData) {
return instance().convertToEntityAttribute(attribute);
}不过,似乎没有什么问题(我相信),我不喜欢这个类坚持使用instance,这就是为什么我要这样做的原因。
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(Function<? super MyAttributeConverter, ? extends R> function) {
MyAttributeConverter referent;
if (reference == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
referent = reference.get();
if (referent == null) {
referent = new MyAttributeConverter();
refernce = new WeakReference<>(referent);
return applyInstance(function);
}
return function.apply(referent); // @@?
}我根本不知道如何测试这段代码。我对我的问题感到抱歉,每个问题都可能有些含糊不清。
reference.get()在function.apply成语中是否有可能是nullSoftReference而不是WeakReference吗?谢谢。
发布于 2020-11-24 13:32:57
注意,这样的方法
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
if (instance == null) {
instance = new MyAttributeConverter();
}
return instance;
}线程是不安全的,因为它包含instance字段的两次读取;每个线程都可能感知到其他线程所做的更新。这意味着instance == null中的第一个读取可能会感觉到另一个线程编写的更新值,而return instance;中的第二个值可以计算为前一个值,即null。因此,当多个线程同时执行时,此方法可以返回null。这是一个罕见的角落情况,但这种方法是不安全的。您需要一个局部变量来确保test和return语句使用相同的值。
// multiple instantiation is not a problem;
private static MyAttributeConverter instance() {
MyAttributeConverter current = instance;
if (current == null) {
instance = current = new MyAttributeConverter();
}
return current;
}这仍然是安全的,只有当MyAttributeConverter是不可变的使用final字段。否则,线程可能返回另一个处于不完全构造状态的线程创建的实例。
您可以使用简单的方法使它不受这些约束而安全:
private static final MyAttributeConverter instance = new MyAttributeConverter();
private static MyAttributeConverter instance() {
return instance;
}这仍然很懒,因为类初始化只发生在指定的触发器之一上,即方法instance()的第一次调用。
您对WeakReference的使用也会遇到同样的问题。此外,还不清楚为什么在局部变量中已有所需参数的两个点上使用递归调用方法。
正确的实现可能要简单得多:
private static WeakReference<MyAttributeConverter> reference;
public static <R> R applyInstance(
Function<? super MyAttributeConverter, ? extends R> function) {
WeakReference<MyAttributeConverter> r = reference;
MyAttributeConverter referent = r != null? r.get(): null;
if (referent == null) {
referent = new MyAttributeConverter();
reference = new WeakReference<>(referent);
}
return function.apply(referent);
}但是在您使用它之前,您应该重新考虑复杂的代码是否值得这么做。当对象被垃圾收集时(甚至可能在并发调用上构建多个实例),您接受了重建对象的需要,这一事实表明,您知道构建成本很低。当构造很便宜时,您可能根本不需要缓存它的实例。
只要考虑一下
public static <R> R applyInstance(
Function<? super MyAttributeConverter, ? extends R> function) {
return function.apply(new MyAttributeConverter());
}至少值得一试,测量应用程序的性能,并将其与其他方法进行比较。
另一方面,它看起来不像是实例占用了大量内存,也没有占用非内存资源。与其他情况一样,您更担心有可能出现多个实例。因此,另一个值得尝试和比较的变体是上面所示的使用static final字段进行延迟类初始化的变体,它没有垃圾收集小对象的机会。
最后一次澄清。你问我
reference.get()在function.apply成语中有可能是null吗?
由于在reference.get()的计算中没有function.apply调用,因此这种调用在这一点上不可能对null进行评估。函数接收一个强引用,并且由于调用代码确保此强引用不是null,因此在调用apply方法期间它永远不会成为null。
通常,垃圾收集器永远不会改变应用程序状态,因为使用强引用的代码会注意到不同的地方(让更多的内存可用)。
但是,由于您专门询问了reference.get(),垃圾收集器可能会在最后一次使用对象与方法执行或局部范围无关之后收集它。因此,当该方法不再使用对象时,可以在执行apply方法时收集该引用。通过查看源代码,运行时优化可能允许这种情况发生得比您可能猜测的要早。,因为看起来像对象使用的东西(例如字段读取)在运行时可能不会使用对象(例如,因为该值已经保存在CPU寄存器中,因此不需要访问对象的内存)。如前所述,这一切都不会改变方法的行为。
因此,假设的reference.get()在执行apply方法时原则上可以评估为null,但是没有理由担心,正如所说的,apply方法的行为不会改变。只要需要,JVM将保留对象的内存,以确保正确的方法执行。
但这一解释只是为了完整。如前所述,对于不保存昂贵资源的对象,不应使用弱引用或软引用。
https://stackoverflow.com/questions/64724264
复制相似问题