当前位置: 首页 > 面试题库 >

关于可见性及时性的volatile的详细语义

易阳云
2023-03-14
问题内容

考虑一个volatile int sharedVar。我们知道JLS为我们提供以下保证:

  1. 写入线程的每个动作w及其价值写入前述isharedVar程序顺序happens-before的写入动作;
  2. 的值写入i通过w happens-before的成功读取isharedVar由读取线程r;
  3. 成功读取isharedVar由读线程r happens-before的所有后续行动r的程序顺序。

然而,仍有给出无挂钟时间的保证 ,当 读线程将观察值i。简单地实现 从未 让读线程看到该值仍符合本合同。

我已经考虑了一段时间,看不到任何漏洞,但我认为一定是有漏洞的。请指出我的推理漏洞。


问题答案:

事实证明,答案和随后的讨论仅巩固了我的原始推理。我现在有一些证据可以证明:

  1. 考虑在写入线程开始执行之前读取线程已完全执行的情况;
  2. 注意此特定运行创建的 同步顺序
  3. 现在将线程按壁钟时间移动,以便它们并行执行,但保持相同的 同步顺序

由于Java内存模型没有引用挂钟时间,因此不会有任何障碍。现在,您有两个线程与 读取线程 并行执行,而 没有观察到写入 线程执行的
任何操作 。QED。

示例1:一次写作,一次阅读线程

为了使这一发现具有最大的意义和真实性,请考虑以下程序:

static volatile int sharedVar;

public static void main(String[] args) throws Exception {
  final long startTime = System.currentTimeMillis();
  final long[] aTimes = new long[5], bTimes = new long[5];
  final Thread
    a = new Thread() { public void run() {
      for (int i = 0; i < 5; i++) {
        sharedVar = 1;
        aTimes[i] = System.currentTimeMillis()-startTime;
        briefPause();
      }
    }},
    b = new Thread() { public void run() {
      for (int i = 0; i < 5; i++) {
        bTimes[i] = sharedVar == 0?
            System.currentTimeMillis()-startTime : -1;
        briefPause();
      }
    }};
  a.start(); b.start();
  a.join(); b.join();
  System.out.println("Thread A wrote 1 at: " + Arrays.toString(aTimes));
  System.out.println("Thread B read 0 at: " + Arrays.toString(bTimes));
}
static void briefPause() {
  try { Thread.sleep(3); }
  catch (InterruptedException e) {throw new RuntimeException(e);}
}

就JLS而言,这是合法的输出:

Thread A wrote 1 at: [0, 2, 5, 7, 9]
Thread B read 0 at: [0, 2, 5, 7, 9]

请注意,我不依赖的任何故障报告currentTimeMillis。报告的时间是真实的。但是,该实现确实选择了使写入线程的所有动作仅在读取线程的所有动作之后才可见。

示例2:两个线程都可以读写

现在,@ StephenC争辩,并且许多人会同意他的观点,即 发生在事前 ,即使未明确提及,仍然 暗示
着时间顺序。因此,我展示了我的第二个程序,该程序演示了这种情况的确切程度。

public static void main(String[] args) throws Exception {
  final long startTime = System.currentTimeMillis();
  final long[] aTimes = new long[5], bTimes = new long[5];
  final int[] aVals = new int[5], bVals = new int[5];
  final Thread
    a = new Thread() { public void run() {
      for (int i = 0; i < 5; i++) {
        aVals[i] = sharedVar++;
        aTimes[i] = System.currentTimeMillis()-startTime;
        briefPause();
      }
    }},
    b = new Thread() { public void run() {
      for (int i = 0; i < 5; i++) {
        bVals[i] = sharedVar++;
        bTimes[i] = System.currentTimeMillis()-startTime;
        briefPause();
      }
    }};
  a.start(); b.start();
  a.join(); b.join();
  System.out.format("Thread A read %s at %s\n",
      Arrays.toString(aVals), Arrays.toString(aTimes));
  System.out.format("Thread B read %s at %s\n",
      Arrays.toString(bVals), Arrays.toString(bTimes));
}

为了帮助理解代码,这将是一个典型的现实结果:

Thread A read [0, 2, 3, 6, 8] at [1, 4, 8, 11, 14]
Thread B read [1, 2, 4, 5, 7] at [1, 4, 8, 11, 14]

另一方面,您永远都不会期望看到类似这样的内容,但是 按照JMM的标准,它仍然是合法的

Thread A read [0, 1, 2, 3, 4] at [1, 4, 8, 11, 14]
Thread B read [5, 6, 7, 8, 9] at [1, 4, 8, 11, 14]

JVM实际上必须 预测 在时间14线程A将写入什么内容,以便知道在时间1让线程B读取什么内容。这种方法的合理性甚至可行性都令人怀疑。

由此我们可以定义JVM实现可以采取的以下 现实 自由:

线程可以将任何不间断的 释放 动作序列的可见性安全地推迟到直到 获取 动作中断它的 获取 动作之前。

术语“ 释放” 和“ 获取”
在JLS§17.4.4中定义。

该规则的一个推论是,可以 无限期推迟 仅写入而从不读取任何内容的线程的操作,而不会违反 事前发生的 关系。

volatile修改实际上是约两个截然不同的概念:

  1. 硬的保证 ,关于它的行动将尊重 之前发生 排序;
  2. 运行时尽力而为,及时发布写入的 软承诺

请注意,JLS并未以任何方式指定点2.,这只是一般预期而已。显然,违反诺言的实现仍然合规。随着时间的流逝,随着我们转向大规模并行体系结构,这一承诺实际上可能会变得非常灵活。因此,我希望将来将保证与承诺的结合证明是不充分的:根据需求,我们将需要一个没有另一个的,具有不同味道的一个或其他任意组合。



 类似资料:
  • 本文向大家介绍java volatile关键字的含义详细介绍,包括了java volatile关键字的含义详细介绍的使用技巧和注意事项,需要的朋友参考一下 java volatile关键字 在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉。 Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入

  • 问题内容: 我在div中有一个HTML 5视频。然后,我有一个自定义播放按钮-效果很好。 我已将视频的可见性设置为在加载时隐藏,并且在单击播放按钮时可见,那么如何在再次单击播放按钮时将其恢复为隐藏状态? 我基本上只是想在可见和隐藏的两种状态之间切换,除非我不能使用切换,因为那是show的隐藏div。我需要在那里,只是隐藏起来,所以它保持正确的高度。 问题答案: 使用jQuery:

  • 我读了SO的以下文章 字段读取同步和易失性之间的区别 发问者写道 同步的目的是确保此线程读取的acct.balance值是当前值,并且对acct.balance中对象字段的任何挂起写入也会写入主存。 最受欢迎的答案: 你是正确的。 请研究此代码: 在我的电脑上,这个程序不会终止。 因此我认为 如果我更改volatile变量,我将在另一个线程中看到任何未完成的地方的实际值 我说得对吗?

  • 我有一个,希望在用户滚动时设置文本并更改的可见性。在用户停止滚动后,应该显示一秒钟,然后停止。为此,我有一个。我的代码如下所示: 代码运行得很好,我只是在想,如果我不应该在中做那么多事情,因为旧的手机可能会有这样的问题。的问题是,即使不需要也会调用它。

  • 当<code>JPanel</code>设置为不可见时,它是否仍然“可触摸”?在我的框架上有一个<code>JPanel</code>,面板上有按钮。如果我将面板设置为不可见,如果我按下按钮所在的位置(如果它可见),该按钮是否仍然有效? 我要求更好地理解,而不是实际上试图实现上面所说的内容。

  • 名为visibility的属性允许您隐藏视图中的元素。 您可以将此属性与JavaScript一起使用,以创建非常复杂的菜单和非常复杂的网页布局。 您可以选择使用visibility属性来隐藏仅在用户需要查看时显示的错误消息,或隐藏测验的答案,直到用户选择一个选项。 NOTE - 请记住,源代码仍将包含隐藏段落中的内容,因此您不应使用此代码隐藏敏感信息,如信用卡详细信息或密码。 visibility