【问题标题】:Local variables and thread safety局部变量和线程安全
【发布时间】:2015-07-07 14:42:59
【问题描述】:

如果你有以下方法,在 Java 中:

public int foo(int n) {
    int i=n;
    i = i+1; i = i-1;
    return i;
}

因此在顺序程序中,返回值将始终与输入相同。

即:j == foo(j)

但是如果你有多个线程调用foo,你能保证j==foo(j)吗?

我会说是的,这是有保证的,因为i 是一个局部变量,每个线程都有自己的堆栈,所以i 将是每个线程的不同内存位置。

如果i 是实例变量,我会说你不能保证j==foo(j)

private int i;
public int foo(int n) {
    i=n;
    i = i+1; i = i-1;
    return i;
}

因为线程可以交错并且i的值可以在线程执行该方法的过程中改变,或者一个线程可以增加i,但在它有机会减少它之前,另一个线程返回它的输入递增两次,仅递减一次。

【问题讨论】:

  • 你的假设是正确的,但我要补充一些更可怕的东西:在你修改实例变量的情况下,绝对不能保证每次递增和递减都会按这个顺序出现线。一个线程可能会看到一个增量,然后是一个减量,另一个线程看到它们以相反的顺序发生。
  • @biziclop 我可以理解多个增量或减量,但我不明白只看到一个减量和一个增量按顺序发生?
  • 规则是In the absence of a happens before ordering between two operations, the JVM is free to reorder them as it pleases. 由于在修改线程中运行的代码和在观察线程中运行的代码之间没有这种关系,所以观察线程可以看到修改的结果任何订单。
  • 这个故事的真正寓意是:当并发访问发生时没有完善的顺序或操作时,不要对程序的行为做出假设。

标签: java multithreading


【解决方案1】:

我会说是的,这是有保证的,因为 i 是一个局部变量,每个线程都有自己的堆栈,所以 i 将是每个线程的不同内存位置。

没错。对foo 的每次调用都是独立的,因为foo 没有使用任何共享状态。

如果 i 是实例变量,我会说你不能保证 j==foo(j)

再次更正。听起来你基本上有正确的想法。 (请注意,即使“递增”和“递减”也不是原子操作,所以如果您有多个线程执行这些操作,您最终会遇到棘手的情况。这就是 AtomicInteger 存在的原因。)

【讨论】:

  • ...即使它们是原子的,可见性仍然是一个问题。这也是AtomicInteger 存在的另一个原因。
  • @biziclop:是的,当然。各种各样的东西:)
  • 谢谢,所以一个问题说“在下面的 Java 代码 [我的问题中的第一个代码],j==foo(j) 适用于顺序程序。但是,如果多个线程调用 foo() 然后 @987654327 @ 可能是假的。解释一下。”没有意义?
  • @Jonathan:是的。对我来说听起来很糟糕。
猜你喜欢
  • 1970-01-01
  • 2017-03-07
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多