java并发在实践中描述了几种确保对象安全发布的方法,其中之一是“分配到正确构造的对象的最后一个字段”。我的问题是,对最终方法参数的赋值是否足以确保安全发布。考虑以下代码:
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()
在后台线程上调用以执行某些任务,并且 someUiMethodCall()
在ui线程上调用以显示结果。
如果我们假设 someBlockingMethodCall()
返回一个可变的、未同步的列表(如arraylist),并将赋值指定给的最后一个参数 displayResults()
确保安全发布列表,还是我需要采取额外的步骤来确保安全发布?
2条答案
按热度按时间vfh0ocws1#
这里有几个误解。这个
final
字段发布保证允许程序员假设不可变对象即使不正确地发布也能正常工作。这并不意味着您应该围绕不正确发布的对象设计软件。通常情况下
runOnUiThread
以及runOnBackgroundThread
应该发布Runnable
示例以线程安全的方式执行线程,因此,不需要依赖final
现场发布。这两种方法是否正确,我们无法判断,因为您没有发布它们的代码。此外,如您正确引用的,该保证适用于“分配给正确构造对象的最终字段”和局部变量(包括参数)不是任何构造对象的字段。但这并不需要担心,因为参数和任何局部变量一样,都是执行方法的线程的局部变量,因此根本不发布。
当您在创建anonymous的示例时捕获局部变量的值时,这里有一个特例
Runnable
实施。这种捕获确实具有final
字段赋值语义,并且无论参数是否已声明final
或者只是最后一个。但是,如前所述,这不应该驱动您的软件设计。你应该确保
runOnUiThread
以及runOnBackgroundThread
一定要正确地发布他们的Runnable,而不是考虑最终的字段发布。如果这些方法不能正确地发布所提供的runnable,那么可能会出现其他问题,但是如果它们发布了,那么runnable引用的任何对象的发布也会正确地发布。这一切都假设您在发布后不修改列表。bwitn5fc2#
你在这里真的很好。唯一一次你真的需要担心它如果你写
results
到未同步的共享变量。要将它传递给线程,通常应该有一些同步。例如,如果启动一个线程
results
传给了runnablestart
你需要的是一个同步点。在本例中,您正在调用
runOnUiThread
,从您提交results
直到有一根线把它接起来。所以这里的提交是线程安全的。