7.3.6 引用队列
在前面的小节中对引用队列做了一些简要的说明。引用队列的主要作用是作为一个通知机制。当对象的可达状态发生变化时,如果程序希望得到通知,可以使用引用队列。当从引用队列中获取了引用对象之后,不可能再获取所指向的具体对象。对于软引用和弱引用来说,在被放入队列之前,它们的引用关系就已经被清除了;而幽灵引用的get方法总是返回null。下面通过分析Java标准库中的WeakHashMap类来说明引用队列在实际中的应用。
WeakHashMap类的实现中的Entry类表示哈希表中包含的条目。Entry类继承自WeakReference类,这样就可以比较方便地处理从引用队列中获取的引用对象,因为引用对象本身代表了哈希表中的条目。Entry类中也包含了与条目相关的基本信息,包括键对象、值对象和引用队列的引用等。Entry类作为一个弱引用,指向的是条目的键对象,而值对象仍然由强引用来指向。在程序的运行过程中,WeakHashMap类的对象中的某些条目的键对象可能变成了弱引用可达的状态。垃圾回收器会清除这些弱引用并将其放入WeakHashMap类的对象的引用队列中。WeakHashMap类的对象需要在合适的时机检查这个队列中包含的引用对象。由于引用对象本身就是Entry类的对象,因此可以直接把引用对象从WeakHashMap类的对象中删除。
删除引用队列中的条目是通过WeakHashMap类中的私有方法expungeStaleEntries来完成的。代码清单7-12给出了核心的方法实现,即通过poll方法检查队列中的引用对象,并将该引用对象转换成Entry类的对象来处理。在WeakHashMap类中,大部分涉及条目的方法的实现都会直接或间接地调用expungeStaleEntries方法来处理WeakHashMap类的对象中引用队列所包含的条目。这也是expungeStaleEntries方法使用poll来检查引用队列的原因。由于对expungeStaleEntries方法的调用会比较频繁,会对WeakHashMap类中正常操作的性能产生影响,因此使用非阻塞式的poll方法是个更好的选择。
代码清单7-12 在WeakHashMap类中删除可被回收的条目的示例
private void expungeStaleEntries(){
for(Object x;(x=queue.poll())!=null;){
synchronized(queue){
Entry<K, V>e=(Entry<K, V>)x;
}
}
}
鉴于WeakHashMap类的实现机制,当其中包含的条目的键对象变成弱引用可达之后,在下一次对WeakHashMap类的对象进行操作时,这些键对象对应的条目才会被删除,所以必须注意这种情况,如果对WeakHashMap类的对象的操作比较少,那么使用WeakHashMap类的对象也会出现某些键对象的存活时间过长的情况。