【问题标题】:Using a local variable when initializing a static variable初始化静态变量时使用局部变量
【发布时间】:2019-04-23 12:12:14
【问题描述】:

java.util.Scanner 的源代码中,我找到了这些静态实用方法:

private static Pattern separatorPattern() {
    Pattern sp = separatorPattern;
    if (sp == null)
        separatorPattern = sp = Pattern.compile(LINE_SEPARATOR_PATTERN);
    return sp;
}

private static Pattern linePattern() {
    Pattern lp = linePattern;
    if (lp == null)
        linePattern = lp = Pattern.compile(LINE_PATTERN);
    return lp;
}

为什么它以如此复杂的方式完成,而不仅仅是,说,

private static Pattern linePattern() {
    if (linePattern == null)
        linePattern = Pattern.compile(LINE_PATTERN);
    return linePattern;
}

在这里使用局部变量 (lp) 有什么意义?这是某种优化技术吗?或者可能是防止同时修改?但是linePattern不能再次设置为null,因为这个方法是唯一修改的地方。

【问题讨论】:

  • 这是目前唯一修改linePattern的地方。但是甲骨文非常擅长编写面向未来的代码。也许未来的JDK版本会有另一种修改linePattern的方式,结合这里的代码(你的版本)可能会导致并发修改问题。
  • 仅作记录。我在java.util.Collections.shuffle(List<?> list) 中发现了类似的构造。那里有一条评论:“无害的种族”。但是,在该过程中初始化的静态变量不是易失性的。

标签: java variables static initialization


【解决方案1】:

我相信这都是关于 volatile 关键字与 linePatternseparatorPattern 字段一起使用

private static volatile Pattern separatorPattern;
private static volatile Pattern linePattern;

由于specification

Java 编程语言允许线程访问共享变量(第 17.1 节)。作为一项规则,为了确保共享变量始终如一且可靠地更新,线程应该通过获取一个锁来确保它对这些变量具有独占性,该锁通常会强制这些共享变量互斥。

Java 编程语言提供了第二种机制,即 volatile 字段,在某些情况下它比锁定更方便。

一个字段可能被声明为 volatile,在这种情况下,Java 内存模型确保所有线程看到变量的一致值

还有here我们可以读到

声明一个 volatile Java 变量意味着:

  • 这个变量的值永远不会被缓存到线程本地:所有的读写都将直接进入“主内存”;

  • 对变量的访问就像它被包含在一个同步块中一样,在其自身上同步。

这就是为什么他们首先尝试直接读取字段的值,然后用新值覆盖它

【讨论】:

  • 假设我的linePattern() 变体,即使变量linePatternlinePattern = Pattern.compile(LINE_PATTERN); 之后和return linePattern; 之前被另一个线程修改,该方法也会(正是因为volatile关键字)返回一个新值linePattern,可以安全使用。也就是说,我没有理由使用局部变量 lp
【解决方案2】:

我认为他们希望避免从 volatile 字段读取两次。

  • 检查一次是否为null
  • 一次用于return

在他们的版本中,他们只

  • 一旦将其读入局部变量
  • 确认局部变量不为空
  • 返回那个局部变量

优化是针对“快乐路径”,它比只发生一次的“初始设置路径”发生得难以置信地多——如果在完成初始化之前同时调用该方法,则最多发生几次。

或者也许预防并发修改?

没有这样的预防措施。如果您在完成设置静态 volatile 字段之前同时调用linePattern,则该模式将被创建多次(但这很好),将返回不同的实例,并且将选择其中一个随机的实例(这也是很好,因为它们是等价的)。

任何防止这种情况的措施只会增加我们的“快乐路径”的成本,所以应该只在实例出于某种原因必须真的是单例的情况下才应该这样做。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-07-21
    • 1970-01-01
    • 2021-10-23
    • 1970-01-01
    • 2023-01-30
    • 2011-08-22
    • 2010-12-22
    相关资源
    最近更新 更多