您的一般结构不是线程安全的
启动整个进程的线程检查myMap
并在可能的另一个线程中启动执行。
if (myMap != null) {
doSomethingInAnotherThread();
}
// something can set `myMap` to null here...
计划运行的代码将在某个时候执行
void inAnotherThread() {
myMap.access();
}
但没有更多的保证myMap
与以前一样。如果有线程可以改变myMap
所指的内容,那么做
void inAnotherThread() {
if (myMap != null) {
myMap.acess();
}
}
仍然不是线程安全的,因为您访问myMap
了两次,并且每次都可能不同。例如,它在内部不为空,if
但null
一旦您访问它。一种解决方案是复制参考,这样当您使用它时,没有任何东西可以更改该参考的本地副本。
void inAnotherThread() {
Map localReference = myMap;
if (localReference != null) {
localReference.acess();
}
}
这是否是线程安全的取决于myMap
. 对于一个是否是volatile
(或final
)。如果是:保证其他线程可以看到所指内容的最新版本myMap
,如果不是:不保证。(注意:我认为runOnUiThread
建立了一个happens-before关系,因为它在内部同步,因此您应该有某种保证可以看到参考的最新版本)
一旦你引用了正确的Map
实例,接下来的一点就是你可以安全地使用它。一个简单HashMap
的不是线程安全的使用。如果您调用.get()
它,如果 - 同时 - 另一个线程调用put
/ remove
/.. 并因此在.get
访问它时更改数据,它仍然可能会炸毁您。
您可以将它包装起来,Collections.synchronizedMap(map)
这样可以使单个操作像get
原子一样,这样其他线程就不会干扰。但它仍然不是所有东西的线程安全。例如,如果您不进行外部同步,则对值进行迭代仍然会失败。这个问题也可以通过使用ConcurrentHashMap
支持迭代的 a 来解决。
线程安全取决于很多因素以及您对什么是线程安全的定义。如果您可以保证myMap
一旦设置为永远不会更改!= null
并且您知道后台线程已完成修改myMap
,那么您编写的内容已经是线程安全的,因此 UiThread 可以安全地访问它。