8

我不能在方法 run() 中使用变量“i”。有什么办法吗?

public class Main {

    public static void main(String[] args) {

         final int M = 100;
         final int N = 4;
         final int[] array = new int[M];

         for(int b = 0; b < M; b++) array[b] = b;

         for( int i = 0; i < N; i++) {
             new Thread(new Runnable() {
                 public void run() {
                         for(int a = i*(M/N);a < (i+1)*(M/N); a++)
                             System.out.println("Thread "+i+":"+array[a]); 
                         // i -> cannot refer to a non-final variable inside an inner class defined in a different method
                     }
             }).start();
         } 



    }   
}
4

4 回答 4

23

您可以声明一个final变量,该变量pi在每次迭代时采用 的值。

    for( int i = 0; i < N; i++) {
       final int p = i;
       new Thread(new Runnable() {
             public void run() {
                  for(int a = p*(M/N);a < (p+1)*(M/N); a++)
                  System.out.println("Thread "+p+":"+array[a]);
             }
        }).start();
     } 

为什么我需要有最终声明?

于 2013-11-10T12:17:40.693 回答
9

这是正确的。这是Java内部类的限制!不允许内部类访问封闭范围内的实例变量,除非该变量声明为final.

所以你需要像这样实现那个代码......

    for (int i = 0; i < N; i++) {
        final int ii = i;
        new Thread(new Runnable() {
             public void run() {
                 for(int a = ii*(M/N);a < (ii+1)*(M/N); a++)
                     System.out.println("Thread "+ii+":"+array[a]);
             }
        }).start();
    }

这种限制的原因是为了避免 Java 需要实现闭包。由于是final,编译器可以通过在实例化匿名内部类时ii将值的副本传递给匿名内部类来实现上述内容。ii这存储在一个隐藏变量中......以便在封闭方法调用完成后可以使用它。

如果没有这个限制,(假设的)Java 编译器将需要创建一个堆对象来保存i变量。(或者至少如果它不能优化对象,它会......)这就是闭包的全部意义......在实现级别。


人们有时会忽略的另一点是,如果 Java 允许您访问 non-final ,则此特定算法将是不正确i的。为什么?因为到线程实际启动时,外部循环很可能已经完成,并且线程都将看到ivalue N。这不是您要在这里实现的目标。


从 Java 8 开始的更新,外部实例变量只需要有效地是最终的。

于 2013-11-10T12:19:00.470 回答
3

尝试:

for( int i = 0; i < N; i++) {
    final currentIndex = i; // declare final here
    new Thread(new Runnable() {
        public void run() {
            for(int a = currentIndex*(M/N) ; a < (currentIndex+1)*(M/N) ; a++)
                System.out.println("Thread " + currentIndex + ":" + array[a]); 
         }
     }).start();
} 
于 2013-11-10T12:18:54.107 回答
3

这就是 Java 内部类的限制,因为 Java 不支持真正的闭包

另请查看此无法引用以不同方法定义的内部类中的非最终变量以获取详细信息。

你可以这样尝试:

for (int i = 0; i < N; i++) {
        final int x = i;
        new Thread(new Runnable() {
             public void run() {
                 for(int a = x*(M/N);a < (x+1)*(M/N); a++)
                     System.out.println("Thread "+x+":"+array[a]);
             }
        }).start();
    } 
于 2013-11-10T12:20:59.840 回答