java - WeakReference 的罕见用法?
问题描述
我有一个类,其实例由底层平面格式初始化和使用。
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
可能null
?- 有没有可能出现内存泄漏等问题?
- 我应该依靠
SoftReference
而不是WeakReference
?
谢谢你。
解决方案
请注意,类似的方法
// 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
当多个线程同时执行此方法时,此方法可能会返回。这是一种罕见的极端情况,但这种方法并不安全。您需要一个局部变量来确保测试和返回语句使用相同的值。
// 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
它永远不会变为。null
apply
通常,垃圾收集器永远不会以使用强引用的代码会注意到差异的方式更改应用程序状态(将更多内存的可用性放在一边)。
但是由于您特别询问reference.get()
,垃圾收集器可能会在对象最后一次使用后收集它,而不管方法执行或本地范围如何。apply
因此,当此方法不再使用对象时,可以在方法执行期间收集所指对象。运行时优化可能会比您通过查看源代码所猜测的更早发生这种情况,因为看起来像对象使用(例如字段读取)可能不会在运行时使用该对象(例如,因为该值已经保存在CPU 寄存器,无需访问对象的内存)。如前所述,所有这些都不会改变方法的行为。
reference.get()
因此,在方法执行期间的假设apply
原则上可以评估为null
,但没有理由担心,如前所述,apply
方法的行为不会改变。JVM 将根据需要保留对象的内存,以确保该方法正确执行。
但这种解释只是为了完整性。如前所述,您不应该对不拥有昂贵资源的对象使用弱引用或软引用。
推荐阅读
- javascript - 当 window.scroll 事件由用户触发或由事件执行的代码触发时识别它
- r - 在箱线图中比较均值时显示“p = 0.1”
- r - 为 LMM 模型中的单个对比计算 AIC/BIC
- ruby-on-rails - 火鸟数据库的活动记录中的布尔字段问题
- c# - 如何检查 LastParagraph 是否存在于 MigraDoc 中
- c# - 通过单击 asp.net 中的按钮在网格中添加多行
- scala - 多个联合后如何解决 Apache Spark StackOverflowError
- php - 停止绕过登录页面
- python - pyqt5 计时器事件在 qthread 中不起作用?
- apache-calcite - 如何使用 Calcite 解析多个 sql 语句