在什么情况下,toSet方法会抛出java.lang.IllegalArgumentException异常?

3
根据我们的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) 返回 -1,但我们无法找到 mapCapacity 的实际实现来验证什么情况会发生这种情况。
有人知道在这里返回 -1 是什么时候,从而导致构造函数失败吗?
1个回答

5

Java集合不是同步的,如果您需要从多个线程访问Map或任何集合,则需要注意同步。如header中所述。

请注意,此实现未同步。如果多个线程同时访问链接的哈希映射,并且其中至少一个线程在结构上修改了映射,则必须在外部进行同步。

我猜测您可能正在对Map执行结构修改(put和remove的混合),但没有同步,这可能会导致此问题。例如:

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 -128SIZE IS -127和许多其他负值,当我在其中一个if块中添加了markersMap.values.toSet()时,就会发生这种情况。

enter image description here


果然是多线程!非常感谢您澄清了这一点。 - Florian Schrofner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接