Невозможно знать наверняка, если вам не удалось получить дамп потока, но однажды я столкнулся с подобной проблемой, когда все 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
), к которому могут одновременно обращаться несколько потоков. Если это так, и это легко сделать, либо замените на ConcurrentHashMap
, либо используйте ReentrantReadWriteLock
для защиты доступа для чтения / записи к карте. Вы, конечно, тоже можете попробовать Collections.synchronizedMap()
, но это не будет таким масштабируемым.
Любое из предложенных исправлений должно предотвратить проблему, если она является основной причиной вашей проблемы.
Смотри также:
http://lightbody.net/blog/2005/07/hashmapget_can_cause_an_infini.html
http://mailinator.blogspot.com/2009/06/beautiful-race-condition.html