为什么 Goetz 不再在代码清单 7.20 中使用 volatile boolean?

Why did Goetz not use volatile boolean for Listing 7.20 again?

这是 Brian Goetz 的 Java 并发实践:

中清单 7.20 的代码
public class CheckForMail {
    public boolean checkMail(Set<String> hosts, long timeout, TimeUnit unit)
            throws InterruptedException { 
        ExecutorService exec = Executors.newCachedThreadPool();
        final AtomicBoolean hasNewMail = new AtomicBoolean(false);

        try { 
            for (final String host : hosts)     
                exec.execute(new Runnable() { 
                    public void run() { 
                        if (checkMail(host)) hasNewMail.set(true);
                    }
                }); 
        } finally { 
            exec.shutdown();
            exec.awaitTermination(timeout, unit); 
        } 
        return hasNewMail.get();
    } 

    private boolean checkMail(String host) { // Check for mail return
        false;
    }
}

参考这段代码,Goetz 说 "The reason an AtomicBoolean is used instead of a volatile boolean is that in order to access the hasMail flag from the inner Runnable, it would have to be final, which would preclude modifying it"(第 158 页)。

为什么 是最终的?你不能把它变成一个非最终的布尔值 volatile 吗?

局部变量不能是可变的。如果您尝试进行该更改,您会发现这是一个编译错误。

顺便说一下,从 Java 8 开始,您不再需要将变量标记为 final;只要它是 effectively final(也就是说,你在第一次之后没有设置它),Java 就会接受它,就像你将它标记为最终的一样。

However why does it have to be final? Couldn't you just make it a non final boolean volatile?

正如 Goetz 所说,

hasNewMail 需要从内部 Runnable 访问。这是一个 内部 class 的实例。在 Java 的最新版本中,变量 "effectively final" 是内部 class 访问词法封闭方法的局部变量的要求。我认为 Goetz 是在要求更强的时候写作的:变量确实是 final。 "effectively final" 和 final 之间的差异对于此目的并不重要,但是,无论哪种方式,都无法修改变量。

还要注意,Goetz 代码中的 AtomicBooleanfinal。它本身无法修改,但存储在中的值可以修改,这就是程序的工作原理。