什么情况下toSet可以抛出java.lang.IllegalArgumentException?
根据我们的 Crashlytics 日志,我们似乎不时遇到以下异常:
Fatal Exception: java.lang.IllegalArgumentException
Illegal initial capacity: -1
...
java.util.HashMap.<init> (HashMap.java:448)
java.util.LinkedHashMap.<init> (LinkedHashMap.java:371)
java.util.HashSet.<init> (HashSet.java:161)
java.util.LinkedHashSet.<init> (LinkedHashSet.java:146)
kotlin.collections.CollectionsKt___CollectionsKt.toSet (CollectionsKt___CollectionsKt.java:1316)
但我们不确定何时可能真正抛出此异常。该语句的相关代码如下所示:
private val markersMap = mutableMapOf<Any, Marker>()
...
synchronized(markersMap) {
val currentMarkers = markersMap.values.toSet() //it crashes here
// performing some operation on the markers
}
现在我们怀疑多线程会导致这个问题,因为markersMap它在多个地方被修改,但是由于地图已经默认初始化,我们不确定它如何最终达到小于空的状态。我们还查看了toSet实现:
if (this is Collection) {
return when (size) {
0 -> emptySet()
1 -> setOf(if (this is List) this[0] else iterator().next())
else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
}
}
基于此,我们假设mapCapacity(size)return -1,但我们无法找到实际实现mapCapacity来验证何时会发生这种情况。
有人知道-1这里什么时候返回,这又会导致构造函数失败吗?
回答
Java 集合不是synchronized,如果您需要Map从多个线程访问一个或任何集合,那么您需要处理同步。如在规定LinkedHashMap的报头
注意这个实现不是同步的。如果多个线程同时访问一个链接的哈希映射,并且至少有一个线程在结构上修改了映射,它必须在外部同步。
我的猜测是您可能在不同步的情况下执行结构修改(put和 和 的混合remove)Map,这可能会导致此问题。例如
fun main(){
val markersMap = mutableMapOf<Any, Any>()
(1..1000).forEach { markersMap.put(it, "$it") }
val t1 = Thread{
(1..1000).forEach { markersMap.remove(it)
if(markersMap.size < 0){
print("SIZE IS ${markersMap.size}")
}
}
}
val t2 = Thread{
(1..1000).forEach {
markersMap.remove(it)
if(markersMap.size < 0){
print("SIZE IS ${markersMap.size}")
}
}
}
t1.start()
t2.start()
}
在我的机器这个代码打印SIZE IS -128,SIZE IS -127以及很多很多其他负值,当我加入markersMap.values.toSet()里面,如果块之一,这事
THE END
二维码