简单服务器的经典示例:
class ThreadPerTaskSocketServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
};
new Thread(task).start();
}
}
}
为什么要将Socket
声明为final
?是因为处理请求的新Thread
可以返回方法中的socket
变量并导致某种ConcurrentModificationException
?
答案 0 :(得分:13)
在这种情况下,变量必须是最终的,才能在匿名Runnable
实施中使用。
这是因为当变量已经超出范围并因此消失时,该对象将存在。该对象获取变量的副本。为了隐藏它,变量必须是最终的,这样任何人都不能指望一个副本中的变化对另一个副本可见。
答案 1 :(得分:3)
考虑这个例子:
class A {
B foo() {
final C c;
return new B() {
void goo() {
// do something with c
}
}
}
}
// somewhere else in the code
A a = new A();
B b = a.foo();
b.goo();
如果c not final,当你到达b.goo()
时,它会指向垃圾,因为那个c会被垃圾收集 - 一个方法调用结束后的一个局部变量
答案 2 :(得分:2)
你需要宣布它是最终的,不仅应该。没有它,编译器就无法在匿名的Runnable类实现中使用它。
答案 3 :(得分:2)
声明方法变量final意味着它的值不能改变;它只能设置一次。在这种情况下,这是如何适用的?
我已经知道有一段时间匿名课程的这种限制,但我从来都不明白为什么。我发现到目前为止,没有其他人能真正做到这一点。一些谷歌搜索出现在下面,我认为很好地解释它。
匿名本地类可以使用本地 变量因为编译器 自动给出一个班级 用于保存副本的私有实例字段 该类使用的每个局部变量。 编译器还添加了隐藏 每个构造函数的参数 初始化这些自动创建 私人领域。因此,一个本地班 实际上并不访问本地 变量,但仅仅是它自己的私有 他们的副本。唯一的方法就是这样 工作正确是如果当地的 变量被声明为final,所以 他们保证不会改变。 有了这个保证到位, 当地的班级确信它的 变量的内部副本 准确反映当地的实际情况 变量
信用: http://renaud.waldura.com/doc/java/final-keyword.shtml#vars
当然不是很明显,我认为编译器应该隐藏在开发人员身上。
答案 4 :(得分:0)
线程之间不共享局部变量。 (局部变量是激活记录的一部分,每个线程都有自己的激活记录)。
由于connection
是局部变量,因此无法在线程之间共享它。由于它不在线程之间共享,因此你需要将其设为final
,因此它是一个局部变量并不重要(它可以看作更像是一个常量值)。
答案 5 :(得分:0)
它无意解决ConcurrentModificationException。必须将方法嵌套类(例如匿名内部类)中使用的任何局部变量声明为final。请参阅上周的类似讨论:
method local innerclasses accessing the local variables of the method
实际上,对于线程,这里对线程安全有一点贡献;线程之间的最终变量不会出现可见性问题。但是,这并不能保证线程安全。