Java:无限while循环中的if语句

发布于 2024-12-22 09:39:19 字数 374 浏览 1 评论 0原文

我是 Java 新手,以下内容可能是显而易见的,但令我感到困惑。考虑以下代码:

while(1>0){
  if(x!=0){
  //do something
  }
}

x 变量在不同的线程中更改。但是,即使 x 不为零,if 语句中的代码也永远不会执行。如果我通过以下方式更改代码,

while(1>0){
  System.out.println("here");

  if(x!=0){
  //do something
  }
}

则当 x 不再为零时,现在将执行 if 语句中的代码。我怀疑这与Java编译器的规则有关,但这让我很困惑。任何澄清这一点的帮助将不胜感激。

I am new to Java and the following might be obvious, but it is puzzling to me. Consider the following code:

while(1>0){
  if(x!=0){
  //do something
  }
}

The x variable is changed in a different thread. However, the code in the if statement is never executed even when x is not zero. If I change the code by the following

while(1>0){
  System.out.println("here");

  if(x!=0){
  //do something
  }
}

the code in the if statement is now executed when x is no longer zero. I suspect this has to do with the rules of the Java compiler, but it is very confusing to me. Any help clarifying this would be greatly appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

月隐月明月朦胧 2024-12-29 09:39:19

如果 x 在不同的线程中发生变化,那么您可能会看到由于未同步对该变量的访问而产生的副作用。

Java 内存和线程模型非常复杂,因此我建议您获取 Java 的副本Brain Goetz 的并发实践,请阅读。

简短的答案是确保对 x 的访问包含在 synchronized 块中:

while (1 > 0) {
    int temp;
    synchronized (this) {
        temp = x;
    }
    if (temp != 0) {
        // Do something
    }
}

在修改 x 的代码中也是如此。

请注意,此示例将 x 存储在临时变量中,因为您希望同步块尽可能小 - 它们强制执行互斥锁,因此您不想在其中执行太多操作。

或者,您可以将 x 声明为 易失性,这可能足以满足您的用例。我建议您使用 synchronized 版本,因为您最终需要知道如何正确使用 synchronized,所以您不妨现在就学习它。

If x is changing in a different thread then you are probably seeing a side-effect of the fact that you have not synchronized access to that variable.

The Java memory and threading model is pretty complex, so I'd recommend you get a copy of Java Concurrency in Practice by Brain Goetz and have a read.

The short answer is to make sure that access to x is enclosed in a synchronized block:

while (1 > 0) {
    int temp;
    synchronized (this) {
        temp = x;
    }
    if (temp != 0) {
        // Do something
    }
}

And similarly in the code that modifies x.

Note that this example stores x in a temporary variable, because you want synchronized blocks to be as small as possible - they enforce mutual exclusion locks so you don't want to do too much in there.

Alternatively, you could just declare x to be volatile, which will probably be sufficient for your use case. I'd suggest you go with the synchronized version because you'll eventually need to know how to use synchronized properly, so you might as well learn it now.

橙味迷妹 2024-12-29 09:39:19

如果您使用多线程代码,请检查 x 变量是否是易失性的。

If you are using multithreading code, check that x variable is volatile.

假扮的天使 2024-12-29 09:39:19

Cameron Skinner 的回答很好地解释了没有 System.out.println("here"); 就不会发生任何事情的原因。

那么为什么当使用 println 时,if(x!=0) 内的块会起作用呢? println 执行一个synchronized 块(请参阅PrintStream.write(String s))。这会强制当前线程从主内存检索 System.out 状态并更新线程的缓存,然后再让线程执行任何进一步的代码行。令人惊讶的副作用是,其他变量的状态(例如您的 x)也会以这种方式更新,尽管 x 的锁不参与同步。这称为搭载

如果我使用自由文本来描述 Java Memory 中描述的形式模型规范:表示在释放锁之前执行的操作发生在在下次获取该锁之后执行的操作。

我将用一个例子来演示它。假设线程 1 已执行,并且只有当它完成时,线程 2 才会启动。还假设 x、y 和 z 是两个线程共享的变量。请注意,我们只能在 ysynchronized 块内确定 z 的值。

Thread 1:
x = 0;
synchronized(y) {
}

Thread 2:
x = 1
z = x;
  // here there's no guarantee as to z value, could be 0 or 1
synchronized(y) {
    z = x;
      // here z has to be 0!
}

这当然是一种非常糟糕的做法来依赖同步......

Th reason nothing happens without the System.out.println("here"); is well explained by Cameron Skinner's answer.

So why does the block inside the if(x!=0) works when println is used? println executes a synchronized block (see PrintStream.write(String s)). This forces the current thread to retrieve System.out state from the main memory and update the thread's cache, before letting the thread to execute any further line of code. The surprising side effect, is that also states of other variables, such as your x, are also updated in this manner, although x's lock was not involved in the synchronization. It's called piggybacking.

If I'll use free text to describe the formalities described in the Java Memory Model Specification: it is said that operations executed before a release of a lock happen-before operations executed after the next obtaining of that lock.

I'll demonstrate it with an example. Assume that Thread 1 is executed and only when it finishes, Thread 2 is started. Also assume that x, y and z are variables shared by both threads. Note that we can determine z's value only inside the synchronized block of y.

Thread 1:
x = 0;
synchronized(y) {
}

Thread 2:
x = 1
z = x;
  // here there's no guarantee as to z value, could be 0 or 1
synchronized(y) {
    z = x;
      // here z has to be 0!
}

This is of course a very bad practice to rely on for synchronization...

佼人 2024-12-29 09:39:19

这可能是编译器优化。它认识到,在 while 循环的范围内,变量永远不会改变,并且会缓存该值,而不是每次都从内存中读取它。为了避免这种行为,只需将变量声明为 volatile:

private volatile int x;

This is likely a compiler optimization. It recognizes that, within the scope of your while loop, the variable never changes and will cache the value rather than reading it from memory each time. In order to avoid this behavior, simple declare the variable as volatile:

private volatile int x;
朮生 2024-12-29 09:39:19

还要检查您在 if 子句中所做的事情。如果它们只是内存操作,那么它比写入控制台要快得多,因此在更改状态之前,您在最后一种情况下执行了更多操作。

Check also what you are doing inside if clause. It they are only memory operations it is a hell lot faster than writing to console, so you are executing much more operations in last case before changing state.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文