Недавно я поднял вопрос в stackoverflow, потом нашел ответ. Первоначальный вопрос заключался в том, какие механизмы, кроме мьютексов или сборки мусора, могут замедлить мою многопоточную Java-программу?
К своему ужасу я обнаружил, что HashMap был изменен между JDK1.6 и JDK1.7. Теперь у него есть блок кода, который заставляет все потоки, создающие HashMaps, синхронизироваться.
Строка кода в JDK1.7.0_10:
/**A randomizing value associated with this instance that is applied to hash code of keys to make hash collisions harder to find. */
transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
Что в итоге вызывает
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
Глядя на другие JDK, я обнаружил, что этого нет в JDK1.5.0_22 или JDK1.6.0_26.
Влияние на мой код огромно. Это делает так, что когда я запускаю 64 потока, я получаю меньше производительности, чем когда я запускаю 1 поток. JStack показывает, что большинство потоков тратят большую часть своего времени на выполнение цикла в случайном порядке.
Так что у меня есть несколько вариантов:
- Перепишите мой код, чтобы я не использовал HashMap, а использовал что-то подобное
- Как-нибудь поиграйте с rt.jar и замените хэш-карту внутри него
- Как-то возиться с путем к классу, поэтому каждый поток получает свою версию HashMap
Прежде чем я начну использовать любой из этих путей (все выглядят очень трудоемкими и потенциально важными), я подумал, не упустил ли я очевидный трюк. Может ли кто-нибудь из вас подсказать, какой путь лучше, или, возможно, определить новую идею.
Спасибо за помощь