据我了解,Java没有真正的闭包。你可以通过chaperoning将一个函数传递给一个类;但是,它不仅冗长而且(由于Java的内存模型)匿名类中对构造它的环境中定义的变量的任何引用都作为副本传递。该语言鼓励我们通过仅允许匿名类引用final
变量来记住这一点。
这让我看到了我在Bloch的 Effective Java 中找到的代码片段:
import java.util.concurrent.*;
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args)
throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while (!stopRequested)
i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
首先,我期望编译器抱怨,因为stopRequested
是非最终的,我在匿名类中引用它。我的编译器没有抱怨。
其次,我期望程序永远循环,因为Java不支持闭包,并且如果匿名类实际上是指它构造的环境中的实际stopRequested
变量(而不是简单的)复制)那么看起来我们这里有一个闭包。 Joshua Bloch还表示,该程序永远在他的计算机上循环。但我的运行大约一秒钟就退出了。
我误解了记忆模型的哪个部分?
答案 0 :(得分:3)
您缺少的关键是匿名类是嵌套类。因此,它具有对包含类的实例的隐式引用,因此具有类的成员。
只有局部变量需要final
才能供匿名类使用。
答案 1 :(得分:1)
它为我循环,原因是CPU缓存,而不是匿名方法。
有了它,它总是会退出:
private volatile static boolean stopRequested;
答案 2 :(得分:1)
首先,我期望编译器抱怨因为stopRequested是 非最终的,我在匿名类中引用它。我的编译器 没抱怨。
stopRequested
是 static
变量。
其次,我希望程序永远循环,因为Java 不支持闭包,如果匿名类真的是 从环境中引用实际的stopRequested变量 是构建(而不是一个简单的副本)然后它似乎我们有一个 在这里关闭。约书亚布洛赫也表示,该计划永远在他的身上循环 电脑。但我的运行大约一秒钟然后退出
stopRequested
不是 volatile
变量。因此,它可能会永远运行(标志提升优化。(以-server
模式运行))。
因此,以下代码
while (!stopRequested)
i++;
可以重新排序为
boolean status = !stopRequested;
while(status)
i++;
答案 3 :(得分:1)
您误解的是,只有局部变量作为副本传递给内部类,因为它们存在于堆栈中,因此在方法调用返回时将消失。
真正的闭包“神奇地”为所有捕获的变量提供了生存的上下文。但对于非局部变量,这不是必需的;它们作为对象或类的一部分存在于堆中,因此Java允许它们是非final的,仍然在内部类中使用。
我认为 Bloch的代码示例应该演示是完全不同的事情:不同的线程可能在其CPU的缓存中具有任何变量(本地,实例或静态)的本地副本,并且更改一个线程可能在任意长时间内对其他线程不可见。为确保同步本地副本,更改必须在synchronized
块/方法中进行,或者必须将变量声明为volatile
。
答案 4 :(得分:0)
您可以按类别通过引用(隐式地OuterClass.this
)或静态字段访问字段。它只是你无法引用的非最终变量。注意:如果此最终引用指向可变的内容,则可以更改它。
final int[] i = { 0 };
new Thread(new Runnable() {
public void run() {
i[0] = 1;
}
}).start();
while(i[0] == 0);
System.out.println("i= " + i[0]);
答案 5 :(得分:0)
stopRequested
不是局部变量,它是一个静态变量,所以它不一定是最终的。程序可能会永远循环,因为stopRequested未声明volatile
,因此无法保证在另一个线程中可以看到由一个线程所做的stopRequested
更改。如果您声明stopRequested
volatile
,该程序将无法永久运行。
期望编译器在这个例子中抱怨是不寻常的。通常的期望是该计划将在开始后很快终止。布洛赫表明情况可能并非如此(这通常使读者感到惊讶)然后解释了原因。布洛赫是一个相当高级的阅读,你可能想先尝试其他关于Java的书。