《Java并发编程实战》Listing6.2中final关键字是否仅用于闭包而非安全发布?
关于《Java并发编程实战》Listing 6.2中final关键字的疑问
先看对应的代码片段:
package net.jcip.examples; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; /** * ThreadPerTaskWebServer * <p/> * Web server that starts a new thread for each request * * @author Brian Goetz and Tim Peierls */ public class ThreadPerTaskWebServer { 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(); } } private static void handleRequest(Socket connection) { // request-handling logic here } }
书中特意加粗强调了final关键字,而你疑惑的是:这里的final是不是仅为了满足匿名类访问变量的需求,和线程安全发布完全无关?
这个问题问得很精准!咱们分两部分说清楚:
直接原因:满足匿名内部类访问局部变量的语法要求
这确实是这里加final的核心目的。在Java的语法规则里,匿名内部类(比如这里的Runnable实现)要访问外部方法的局部变量时,这个变量必须是final的(或者Java 8及以后的「有效final」——即变量声明后没有被修改过)。
背后的逻辑是:匿名类会在创建时复制一份局部变量的副本到自己的内部,final保证原变量不会被后续修改,避免匿名类持有的副本和原变量出现不一致的情况,防止逻辑混乱。附带效果:间接实现了线程安全发布
虽然这不是加final的初衷,但它确实顺带达成了线程安全发布的效果。根据Java内存模型(JMM)的规则,final修饰的变量在构造完成后会被安全发布——也就是说,当我们启动新线程执行task时,新线程能确保看到connection变量的完整、正确的状态,不会出现未初始化的可见性问题。
不过要明确:这是一个额外的收益,不是编写这段代码时加final的主要动机。
总结一下:这里的final首先是为了绕过Java的语法限制,让匿名类能访问connection变量;但与此同时,它也意外地帮我们满足了线程安全发布的要求,算是一举两得,但核心原因还是前者。
内容的提问来源于stack exchange,提问作者bhh1988
相关产品推荐
相关产品推荐

