3

我想深入了解 Java 是如何工作的。所有教程的水平都太高了,所以我有几个问题:

  1. 鉴于此代码:

    class Example {
      public void foo() {
        int number = getRandomNumber();
        System.out.println(number); 
      }
    }
    

    假设在上面的例子中,foo()方法可以被多个线程访问。每个线程是否都有自己的正确number变量值,或者第二个线程是否可以修改第一个线程的值(因此它们都打印相同的值)?

  2. 如果int number是决赛会发生什么?

我想知道的主要事情是,当我进行一些初始化(连接,...)时,我想确保线程不会相互干扰。

4

8 回答 8

6

每个线程都有自己正确的数字变量值,还是第二个线程可以修改第一个线程的值(所以它们都打印相同的值)?

将其视为每个方法调用foo都有自己的变量会更清楚——它是方法中number的局部变量。因此,如果这是某种递归方法,即使在单个线程中number,您也可能会同时使用多个变量,因为它们是该方法的不同调用。

没有其他线程可以更改局部变量的值,您也无法更改任何其他代码中一次调用的值。这是调用状态的一部分。

如果 int 数是 final 会发生什么?

基本上,这将阻止您在初始化后更改变量的值。

于 2013-04-16T10:17:57.750 回答
2

方法中的所有本地成员都将在堆栈上,因此每个方法都有自己的数字变量副本。

如果数字是最终的,那么每个线程仍然会有自己的数字副本。声明最终只会告诉编译器这个变量不会改变,所以编译器可以在他这边进行优化。

于 2013-04-16T10:16:48.607 回答
1

方法中声明的所有变量都是堆栈本地的。进入该方法的每个线程都将为那些对其他线程完全不可见的变量获取自己的值。声明变量 final 不会改变这一点……它只会影响您是否可以访问这些变量,例如,在匿名内部类中。

现在,您可能会对从该方法检索到的值有一些并发问题getRandomNumber——这取决于它的实现以及它是否访问共享(类)状态。但那是理论,因为你没有展示它的实现。

对于咧嘴笑和咯咯笑,这是显示本地方法堆栈的字节码:

public class MyClass {
    public void myMethod() {
        int tField = 10;
        System.out.println(tField);
    }
}

字节码:

   public void myMethod();
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1         // <-- Each thread gets its own
         0: bipush        10
         2: istore_1      
         3: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: iload_1       
         7: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        10: return        
于 2013-04-16T10:16:14.103 回答
1
  1. 每个线程都有自己的局部变量副本,因为每个线程都有自己的调用堆栈。
  2. 您将无法在方法的其他地方为该最终变量分配任何其他值。
于 2013-04-16T10:16:18.197 回答
1

number是一个局部变量,而不是一个字段,所以它存在于堆栈中,每个线程都有自己的版本。

您只需要考虑共享状态的线程安全性,即在堆上分配的数据,例如对象的字段。

于 2013-04-16T10:16:32.270 回答
1

在回答您的两个问题时number- 每个线程都有自己的变量 - 没有线程能够改变任何其他线程的局部变量,无论它是否final存在。

所有局部变量都是唯一的 - 每个调用该方法的线程在堆栈上都有一个副本。

于 2013-04-16T10:16:40.870 回答
1

每个线程都有自己的调用堆栈。

在该调用堆栈上有堆栈帧。每个方法调用一个。

在该堆栈框架内是局部变量(除其他外)。

所以是的,foo方法的每次调用都有自己的number变量。

拥有一个final局部变量并不会从根本上改变这一点。

于 2013-04-16T10:17:26.847 回答
1

根据示例代码

  1. 在方法上下文中,并发访问的线程foo()将有自己的堆栈来执行foo()方法。局部变量不共享。

  2. 就并发访问而言,没有任何问题。拥有它最终将确保内部foo()方法编号值不会更新。

如果对象或静态变量在多个线程之间共享,则会出现并发场景并且线程可能会干扰。

下面如果 XYZ 类的对象在两个线程之间共享,则由一个线程更新的数字可能会由第二个线程打印。

 class XYZ {
static int number = 0;
  public void foo() {
    number = getRandomNumber();
    System.out.println(number); 
  }
}
于 2013-04-16T10:22:59.103 回答