You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

《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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:15:20