【问题标题】:Thread safety of Java example using WeakReference使用 Wea​​kReference 的 Java 示例的线程安全
【发布时间】:2011-06-17 08:06:53
【问题描述】:

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

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

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”弱引用的引用。这会创建对所指对象的强引用,因此即使在空检查之后,对 readFile 范围之外的所指对象的所有其他强引用都消失了(正确的术语是什么?),即使假设所指对象不可软访问,垃圾收集器也不会允许清除弱引用,从而使所指对象可终结,因为仍然存在来自数据的本地强引用。因此,如果data != null 行中的数据不为空,则在下一行中返回时它不能为空。对吗?

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

    • 我将创建一个将文件和数据都保存为最终变量的类,而不是同步。然后对该类进行可变引用。然后 readFile 可以获得对类引用的本地引用并在整个方法中使用它。然后在完成后更新实例字段。
    • @Michael 你是对的,这绝对是要走的路。我只是想指出让它工作的最简单方法。
    【解决方案2】:

    一旦将引用分配给本地 data 变量,该对象就没有资格进行垃圾回收。它是strongly reachable,因为在线程堆栈中有对它的引用。

    【讨论】:

    • 谢谢。我想我使这两行过于复杂了。只需注意对数据的强烈引用就足够了。关于强可达:似乎有两个定义在使用。您引用的那个:“如果某个线程可以在不遍历任何引用对象的情况下访问该对象,则该对象是强可达的。”其次来自Java规范:“可达对象是可以从任何活动线程的任何潜在持续计算中访问的任何对象”这些是否等效并且是否存在严格定义第二个语义的地方?我觉得规格并不总是准确的
    【解决方案3】:

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

    【讨论】:

    • 我认为讨论线程纯粹是因为垃圾收集,并且readFile 不会跨线程调用。你当然是对的。
    • 我想补充一点,使字段易失也无济于事,因为它会允许文件数据关联不同步,因此可能返回错误的数据,因为文件字段已更新,而引用尚未更新。
    猜你喜欢
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 1970-01-01
    • 2016-11-29
    • 1970-01-01
    相关资源
    最近更新 更多