为什么传递给runnable的变量需要是最终的?

如果我有一个变量int x = 1 ,比如说,我在主线程中声明了一个runnable,并且我想将x传递给runnable的run()方法,那么它必须被声明为final 。 为什么?

 final int x = 0;//<----must be final... private class myRun implements Runnable { @Override public void run() { x++;// } } 

因为如果它们能够被改变,它可能会导致很多问题,请考虑这个:

 public void count() { int x; new Thread(new Runnable() { public void run() { while(x < 100) { x++; try { Thread.sleep(1000); }catch(Exception e){} } } }).start(); // do some more code... for(x = 0;x < 5;x++) for(int y = 0;y < 10;y++) System.out.println(myArrayElement[x][y]); } 

这是一个粗略的例子,但你可以看到可能发生很多无法解释的错误。 这就是变量必须是最终的原因。 以下是针对上述问题的简单修复:

 public void count() { int x; final int w = x; new Thread(new Runnable() { public void run() { int z = w; while(z < 100) { z++; try { Thread.sleep(1000); }catch(Exception e){} } } }).start(); // do some more code... for(x = 0;x < 5;x++) for(int y = 0;y < 10;y++) System.out.println(myArrayElement[x][y]); } 

如果你想要一个更完整的解释,它有点像同步。 Java希望阻止您从多个线程引用一个Object。 这里有一点关于同步:

希望这有帮助!

因为这就是语言规范所说的。 根据Guy Steele的说法 ,这种选择背后的基本原理是程序员会期望方法中的声明int x = 0导致堆栈分配的存储,但是如果你可以从方法返回一个new myRun() (或者让一个myRun持续超过函数的返回值,然后你可以修改它,然后x必须被堆分配,而不是你想要的语义。

他们本可以做到这一点,事实上其他语言就是这样做的。 但Java设计者决定要求将x标记为final以避免要求实现堆分配看起来像堆栈分配的存储。

(我应该注意:这不是特定于Runnable 。它适用于任何匿名内部类。)

multithreading的重大“问题”以及使用它的全部原因是同时发生了多件事。 突然之间,线程访问的任何变量的值都不是线程本地的,可以随时改变。 因此,您可能只需使用以下代码打印数字1-10:

 int x = 0; //supposing that this was allowed to be non-final... private class myRun implements Runnable{ @Override public void run() { for (int i=0; i<10; i++ ) { System.Out.Println( x++ ); } } } 

但实际上,如果该类中的其他代码更改了x的值,则最终可能会打印230498 - 230508.x的值可能会在循环中间发生事件更改。 如果您不能依赖具有特定值的x或保留之前分配给它的值,则在代码中使用它将变得徒劳无功。 如果变量的内容可能会随着帽子的变化而变化,为什么还要使用变量?

Java要求你让它成为final ,而不是仅仅禁止你使用它。 您可以“承诺”永远不会从另一个线程更改x的值,但为什么不首先让它成为final并让编译器帮助您? 当然,你只能访问分配给x的初始值,但只是能够访问变量的初始值比完全不能使用它更好,这将有效地切断线程利用其余数据的能力。你的class级。