【问题标题】:Java Thread Synchronized methodsJava 线程同步方法
【发布时间】:2016-09-02 20:18:51
【问题描述】:

我有以下代码

import java.util.concurrent.*;
public class dsd {

 private static boolean stopRequested;
 private static void requestStop() {
  stopRequested = true;
 }
 private static synchronized boolean stopRequested() {
  return stopRequested;
 }
 public static void main(String[] args)
 throws InterruptedException {
  Thread backgroundThread = new Thread(new Runnable() {
   public void run() {
    int i = 0;
    while (!stopRequested())
     i++;
   }
  });
  backgroundThread.start();
  TimeUnit.SECONDS.sleep(1);
  requestStop();
 }
}

问题是为什么即使requestStop() 没有同步它也能工作?如果我尝试对stopRequested() 做同样的事情,它就不再起作用了。为什么该变量上的线程并发没有问题?我知道同步使变量出现在其他线程的一致状态中。但是这里的变量没有同步,似乎没有效果。

【问题讨论】:

  • ...为什么有效... 如果您说出“有效”的含义,将会很有帮助。这并不总是显而易见的,尤其是对于像您这样不产生任何输出的程序。我猜测“工作”意味着程序在一秒钟后很快就停止了。
  • 你需要同步(或不稳定)来保证你想要的行为,但如果你没有它,如果你幸运的话,你可能仍然会得到它(小像这样的程序更容易幸运)。换句话说,“你不能保证 X”不同于“你不能保证 X”。
  • 如果我尝试对 stopRequested() 做同样的事情... 如果您尝试做同样的事情什么?你做了什么?你做了什么?你是说如果你在stopRequested()的声明中加上synchronized
  • “用同样的东西”我在第二种方法中提到了扔掉同步

标签: java multithreading synchronized


【解决方案1】:

synchronized 是可重入的,获得同步块锁的同一个线程保证如果它仍然拥有锁,它会在下一次尝试获取时保持它,但这与事实上,stopRequested 的更改是在同步块之外进行的,并且代码仍然有效,您的代码在实际情况下仍然可能存在竞争条件。

假设在执行N 时,线程必须在满足stopRequested() 的条件下终止,同时调用requestStop(),不能保证线程在N+1 处终止,因为变量@ 987654327@ 在两种访问器方法中都不受排他锁的保护,变量也没有volatile 修饰符。这意味着由于竞争条件,您对代码的正确感部分是错误的。

【讨论】:

  • thread-safety的光照下并不完全正确。它可能有条件损坏,就像@yshavit 说的那样。 small programs like this make it easier to be lucky 可能会运行很长时间没有出现错误的迹象,但总有一天会出现。
  • 通常人们说“竞争条件”是指程序的正确输出取决于按特定顺序发生的事情,而程序中没有任何东西可以保证它们按该顺序发生。在这个程序中,唯一的“输出”是程序最终会停止。
  • 不是这样,但受影响的变量i 可能在while-loop 的末尾具有非预期值。
【解决方案2】:

Java 语言规范没有说明该程序是否或何时终止。

只有当requestStop()stopRequested() 这两种方法 同步时,您才能确定它将在大约一秒后终止。

如果其中任何一个不同步,或者两者都不同步,则程序可能会在一秒后终止,或在五秒后终止,或从不终止。它在不同的 Java 运行时环境中可能表现不同。它在不同的硬件上可能表现不同。它可能在一周中的不同日子表现不同。

如果这两种方法都不是synchronized,它的行为是不确定的。

【讨论】:

    【解决方案3】:

    synchronized 块在 Java 中具有额外的语义。它们不仅提供代码块的独占执行,还发出所谓的内存屏障,以保证线程之间所有操作结果的可见性。

    因此,当您调用synchronized stopRequested() 方法时,您也会使所有更改对当前线程可见。如果你从这个方法中删除synchronized,你也删除了障碍,JVM 不能保证你在主线程中设置的标志在后台线程中是可见的。这就是为什么没有synchronized 会有问题。

    【讨论】:

      【解决方案4】:

      Java Concurrency in Practice的前言中引用了Dion Almaer的一句话:

      TheServerSide 的前编辑 Dion Almaer 最近在博客中写道(经过一段痛苦的调试会议,最终发现了一个线程错误),大多数 Java 程序都充满了并发错误,以至于它们只能“偶然”工作。

      这是其中之一。 Java 语言规范不承诺更新的标志将是可见的。如果您不遵守规则,那么您将受到 JVM 实现的支配,以确定您的线程是否曾经看到您的标志的新值。

      一些 JVM 实现比其他实现更宽容,一些被设计为更积极地处理它们如何缓存内容。如果它在本地对您有用,并不意味着它可以在生产中使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-02-10
        • 1970-01-01
        • 2013-11-12
        • 1970-01-01
        • 2012-11-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多