安全使用WeakReference

3

假设我有一个名为Publisher的类,其中有一个存储在WeakReference<>列表中的Subscriber对象列表。

public interface Subscriber {
    void update();
}

public class Publisher {
    private final List<WeakReference<Subscriber>> subscribers = new CopyOnWriteArrayList<>();

    public void subscribe(final Subscriber subscriber) { 
        subscribers.add(new WeakReference<>(subscriber)); 
    }

    public void publish() { ...

在调用 Publisher::subscribe 和稍后调用 Publisher::publish 之间,弱引用列表中的一个 Subscriber 可能已经被垃圾回收,因此在使用它之前需要检查是否为 null
我的问题是下面的代码是否是一个安全的 publish 实现?
public void publish() { 
    //filter out garbage collected items
    subscribers = subscribers.stream()
            .filter(sub -> sub.get() != null)
            .collect(Collectors.toList());
    //use the remaing objects
    for (final WeakReference<Subscriber> sub : subscribers) {
        sub.get().update());
    }
}

在过滤subscribers和调用Subscriber::update之间,垃圾回收器是否可能销毁了另一个对象? 在更新时,我是否应该执行第二个null检查?

    for (final WeakReference<Subscriber> sub : subscribers) {
        if (sub.get() != null) {
            sub.get().update());
        }
    }
1个回答

5

你提出的第二个空值检查也不够好,因为第一次调用 get() 可能会返回一个非空值,而第二次调用可能会返回 null。我建议:

for (WeakReference<Subscriber> subRef : subscribers) {
    Subscriber sub = subRef.get();
    if (sub != null) {
        sub.update();
    }
}

或者使用 Java 8 的流(未经测试):

subscribers
    .stream()
    .map(WeakReference::get)
    .filter(s -> s != null)
    .forEach(Subscriber::update);

那么如果我理解正确的话,垃圾收集器可以在执行的任何行中执行清理操作? - flakes
@flkes:是的,如果没有其他东西在维持它的运行。 - Jon Skeet
GC 无法在 get() 后立即销毁对象,只要被 get() 返回的强引用仍然在范围内。 - Alex Cohn
@AlexCohn:我不太确定你在说什么,但重点是在 OP 的代码中,他们调用了 get() 并检查结果是否为 null,但随后又再次调用了 get(),这并不能保证在那时它仍然是非 null 的。 - Jon Skeet
是的,这个 if (sub.get() != null) { sub.get().update()); } 模式不好。 - Alex Cohn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接