除非您设法获得线程转储,否则无法确定,但是一旦我遇到了类似的问题,即所有 8 个内核都以 100% 的速度忙于数千个线程(但它不在 Tomcat 上)。
在我们的例子中,每个线程都被困java.util.HashMap
在 get() 方法中,在 for 循环中紧紧地旋转:
public V get(Object key) {
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
for (Entry<K,V> e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
我们的理论是,特定存储桶中的条目链接列表不知何故已损坏并指向自身,因此永远无法退出循环。由于没有任何工作完成,随着请求的增加,越来越多的线程从池中被消耗。
如果在放置新条目时必须调整表的大小,但多个线程存在不受保护的读/写访问,则可能会发生这种情况;一个线程可能正在扩展特定存储桶位置的链表,而另一个线程正忙于尝试移动它。如果对哈希映射的访问不同步,那么它很可能会损坏(尽管通常不可重现)。
检查是否有多个线程可以同时访问的共享HashMap
(或)。HashSet
如果是这样,并且很容易做到这一点,要么用 a 替换ConcurrentHashMap
,要么使用 aReentrantReadWriteLock
来保护对地图的读/写访问。你当然也可以尝试Collections.synchronizedMap()
,但这不会那么可扩展。
如果事实证明这是您的问题的根本原因,任何这些建议的修复程序都应该可以防止该问题。
也可以看看:
http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html
http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html