使用WeakReference的Java示例的线程安全性

时间:2011-06-17 08:06:54

标签: java garbage-collection thread-safety weak-references

我正在阅读一篇SO帖子后读到Java中的弱引用,并意识到我并不知道它们是什么。

以下代码来自第457页,第17章:“Java编程语言,第四版”中的“垃圾收集和内存”,作者是Arnold,Gosling和Holmes

import java.lang.ref.*;
import java.io.File;

class DataHandler {
    private File lastFile;        // last file read
    private WeakReference<byte[]> 
                         lastData;// last data (maybe)

    byte[] readFile(File file) {
        byte[] data;

        // check to see if we remember the data
        if file.equals(lastFile) {
            data = lastData.get();
            if (data != null)
                return data;
        }

        // don't remember it, read it in
        data = readBytesFromFile(file);
        lastFile = file;
        lastData= new WeakReference<byte[]>(data);
        return data;
    }
}

我试图理解,只是为了练习它,如果这段代码是线程安全的,那部分代码我专注于成为行

data = lastData.get();
if (data != null)
    return data;

我的想法如下:“数据”是线程限制的,并设置为引用“lastData”WeakReference的引用。这会创建一个对引用的强引用,因此即使在null检查之后,对readFile范围之外的所有其他强引用也会消失(正确的术语是什么?),即使假设引用不可轻易到达,垃圾收集器也不会允许清除弱引用,从而使指示物最终化,因为仍然存在来自数据的本地强引用。因此,如果data != null行中的数据不为空,则在以下行中返回时,它不能为null。正确吗?

3 个答案:

答案 0 :(得分:2)

一旦将引用分配给本地data变量,该对象就不符合垃圾回收的条件。它是strongly reachable,因为在线程的堆栈中有对它的引用。

答案 1 :(得分:2)

我认为示例代码线程安全,但出于与使用弱引用不同的原因:

弱引用的用法很好,完全符合您指出的原因:代码创建了一个强引用,该引用保存在data变量中。因此,GC无法收集字节,因此WeakReference也将保持不变;所以在单线程应用程序中使用此代码应该是安全的。问题来自多线程:

filelastData字段的访问已同步,因此无法保证两个使用readFile(..)方法的线程完全相互作用(这不太可能是“最好”的情况)。重要的是要注意这些字段必须以原子方式访问,如果没有其他地方触及它们,最简单的解决方法是声明同步readFile方法。这会严重影响性能,因为文件读取会在同步块内发生,可能导致不良争用。

答案 2 :(得分:-1)

从技术上讲,它不是线程安全的,因为lastData和lastFile不是volatile。第二个线程可以查看这些引用的旧副本。它可能对您的应用程序没有太大影响。