Java Concurrency in Practice 描述了几种确保对象安全发布的方法,其中之一是“分配给正确构造的对象的最终字段”。我的问题是,最终方法论证的赋值是否足以确保安全发布。请考虑以下代码:
private void collectResults() {
runOnBackgroundThread(new Runnable() {
public void run() {
displayResults(someBlockingMethodCall());
}
});
}
private void displayResults(final List<Foo> results) {
runOnUiThread(new Runnable() {
public void run() {
someUiMethodCall(results);
}
});
}
每个方法将Runnable
排队等待在另一个线程上执行。在后台线程上调用someBlockingMethodCall()
来执行某项任务,并在UI线程上调用someUiMethodCall()
来显示结果。
如果我们假设someBlockingMethodCall()
返回一个可变的,不同步的列表(例如ArrayList),则会对displayResults()
的最终参数进行赋值,以确保列表安全发布,或者我是否需要确保安全发布的额外步骤?
答案 0 :(得分:3)
你真的很好。如果将results
写入未同步的共享变量,则唯一需要真正担心的时间。
要将其交给线程,通常应该有一些同步。例如,如果您启动了一个帖子并且results
已传递给Runnable,则该帖子的start
就是您需要的同步点。
在这种情况下,您正在调用runOnUiThread
,从您提交results
到线程获取它的那一刻有一个同步点。
所以这里的提交将是线程安全的。
答案 1 :(得分:0)
这里有几个误解。 this
字段发布保证允许程序员正确地使用不可变对象,即使它们被不正确地发布。这并不意味着您应该围绕不正确发布的对象设计软件。
通常,public A b(string a)
{
//Do stuff
return this;
}
和final
应该以线程安全的方式将runOnUiThread
实例发布到正在执行的线程,因此,不需要依赖{{1}字段发布。这两种方法是否正确,我们不可能告诉你,因为你没有发布他们的代码。
此外,正如您正确引用的那样,保证适用于“对正确构造的对象的最终字段的赋值”,而局部变量(包括参数)不是任何构造对象的字段。但这并不值得担心,因为参数是执行该方法的线程的任何局部变量 local ,因此根本不会发布。
这里有一个极端情况,因为您在创建匿名runOnBackgroundThread
实现的实例时捕获局部变量的值。这种捕获确实具有Runnable
字段赋值语义,无论参数是否已被声明final
或只是有效最终。
但是,如上所述,这不应该推动您的软件设计。您应确保Runnable
和final
正确发布其runnable,而不是考虑最终的字段发布。如果这些方法没有正确发布提供的runnables,则可能会出现其他问题,但如果这样做,则runnables引用的任何对象的发布也会正确发布。这一切都假设您在发布后不修改列表。