Thread的run()方法中的最终枚举

时间:2010-03-28 12:05:39

标签: java multithreading enums final

为什么Elvis elvis定义必须是最终才能在Thread run()方法中使用?

 Elvis elvis = Elvis.INSTANCE; // ----> should be final Elvis elvis = Elvis.INSTANCE
 elvis.sing(4);

 Thread t1 = new Thread(
   new Runnable() {
   @Override
   public void run() {
   elvis.sing(6); // --------> elvis has to be final to compile
  }
}
);


 public enum Elvis {
   INSTANCE(2);

   Elvis() {
     this.x = new AtomicInteger(0);
   }

   Elvis(int x){
     this.x = new AtomicInteger(x);
   }

   private AtomicInteger x = new AtomicInteger(0);

   public int getX() { return x.get(); }

   public void setX(int x) {this.x = new AtomicInteger(x);}

   public void sing(int x) {
      this.x = new AtomicInteger(x);
      System.out.println("Elvis singing.." + x);
   }
 }

3 个答案:

答案 0 :(得分:3)

匿名内部类正在捕获elvis变量的值。

仅限Java(当前)按值捕获变量。编译器要求变量是final的,以便在新线程中调用run方法时实际使用的内容不会混淆:如果在创建后更改了elvis的值新线程但在开始之前,你期望它做什么?

这是C#和Java中有效闭包的方式之间的区别。有关详细信息,请参阅我的closures article。 Java 7将使闭包更简洁 - 我一直没有跟踪知道是否有任何方法来捕获变量本身而不是特定值。

答案 1 :(得分:2)

这与线程无关,与构建匿名类有关。问题是您正在匿名类中引用局部变量。现在考虑以下内容:

int c = 5;
Runnable r = new Runnable(){ public void run(){ System.out.println(c); } };
c = 6;
r.run();

在上面的代码段中,代码是打印5还是打印6?如果r要保持对当前堆栈帧的引用以便解析c,则可以想象它可以打印6.也可以想象它可以更早地绑定/捕获c的值并打印5. Java强制你要做最后的决定,以便使这个完全清楚,并且还要解除Java挂在当前堆栈框架上的需要。

答案 2 :(得分:1)

这不是你问题的一部分,但我只是好奇:如果它是AtomicInteger,你为什么要重新分配Elvis.x?那种错过了AtomicInteger的线程安全性。考虑重写:

public Elvis {

   final static private Elvis INSTANCE = new Elvis(2);
   static public Elvis getInstance() { return INSTANCE; }

   final private AtomicInteger x; 


   Elvis() { this(0); }

   Elvis(int x){ 
      this.x = new AtomicInteger(x);
   }

   public int getX() { return this.x.get(); }

   public void setX(int x) {this.x.set(x); }

   public void sing(int x) {
      setX(x);
      System.out.println("Elvis singing.." + x);
   }
 }

因为这有可变的内容,所以它不应该是枚举。