为什么我应该在多线程程序中"最终"共享变量

use*_*791 2 java concurrency

我的问题是为什么我应该使用final来装饰变量,列表?它由匿名内部类的实例使用而不是final,它不会编译.

代码如下所示:

public class TwoThreadsOneListCurrModi
{
  public static void main(String[] args)
  {    
     final List<String> list = Collections.synchronizedList(new ArrayList<String>());

    for (int i =0 ; i<20;i++)
      list.add(String.valueOf(i));
    Thread t1 = new Thread(new Runnable(){

      @Override
      public void run()
      {
          synchronize(list) {
          System.out.println("size of list:" +list.size());
          }
      }
    });

    t1.start();  
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我使用普通课,那很好.

public class TwoThreadsOneListCurrModi2

{
  public static void main(String[] args)
  {    
     final List<String> list = Collections.synchronizedList(new ArrayList<String>());
    initialize list;

    Thread t1 = new WorkThread(list);
    Thread t2 = new WorkThread(list);    
    t1.start();  
    t2.start();
  }
}
class WorkThread extends Thread{
    List<String> list;
    public void run(){
       do sth with list and synchronize block on list
  }
  Work1(List<String> list)
  {    this.list = list;  }
}
Run Code Online (Sandbox Code Playgroud)

Ada*_*old 11

这与多线程无关.它就在那里,因为你试图list从一个匿名的内部类方法访问.在这种情况下,Java将始终签署错误.

在您的情况下,您Runnable使用new关键字创建此处的匿名实例.你想要取消引用的一切都run需要final.

如果您对最终关键词的必要性感到好奇,可以查看Jon Skeet的详尽答案,深入解释.

关键在于,当您创建匿名内部类的实例时,在该类中使用的任何变量都会通过自动生成的构造函数复制它们的值,如果该变量可以被该方法的其余部分修改,则看起来很奇怪反之亦然.

  • 您可能想要提到变量最终需要的方法,关于Java在将变量提供给匿名内部类之前创建变量的副本等等... (3认同)
  • 您在答案中的引用不正确,几乎已在Java社区中获得城市传奇地位.它与从堆栈中清除的局部变量无关.Java对象是在堆上创建的,而不是堆栈,而原语只是传递的值.相反,它与Java中缺少闭包有关,而Java需要在将变量传递给匿名类之前复制变量.请阅读[Jon Skeet关于这个问题的答案](http://stackoverflow.com/a/4732617/522444)并引用他,如果你要引用任何人的话. (2认同)
  • 谢谢.上投了反对票. (2认同)