Мне нужно собрать статистику в моем программном обеспечении, и я пытаюсь сделать это быстро и правильно, что непросто (для меня!)
сначала мой код с двумя классами, StatsService и StatsHarvester
public class StatsService
{
private Map<String, Long> stats = new HashMap<String, Long>(1000);
public void notify ( String key )
{
Long value = 1l;
synchronized (stats)
{
if (stats.containsKey(key))
{
value = stats.get(key) + 1;
}
stats.put(key, value);
}
}
public Map<String, Long> getStats ( )
{
Map<String, Long> copy;
synchronized (stats)
{
copy = new HashMap<String, Long>(stats);
stats.clear();
}
return copy;
}
}
это мой второй класс, харвестер, который время от времени собирает статистику и записывает ее в базу данных.
public class StatsHarvester implements Runnable
{
private StatsService statsService;
private Thread t;
public void init ( )
{
t = new Thread(this);
t.start();
}
public synchronized void run ( )
{
while (true)
{
try
{
wait(5 * 60 * 1000); // 5 minutes
collectAndSave();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
private void collectAndSave ( )
{
Map<String, Long> stats = statsService.getStats();
// do something like:
// saveRecords(stats);
}
}
Во время выполнения он будет иметь около 30 одновременно работающих потоков, каждый из которых будет вызывать notify(key)
около 100 раз. Только один StatsHarvester вызывает statsService.getStats()
Так что у меня много писателей и только один читатель. было бы неплохо иметь точную статистику, но мне все равно, будут ли потеряны некоторые записи при высоком параллелизме.
Читатель должен запускаться каждые 5 минут или что-либо разумное.
Письмо должно быть максимально быстрым. Чтение должно быть быстрым, но если оно блокируется примерно на 300 мс каждые 5 минут, это нормально.
Я прочитал много документов (параллелизм Java на практике, эффективный Java и т. Д.), Но у меня есть сильное чувство, что мне нужен ваш совет, чтобы сделать это правильно.
Надеюсь, я изложил свою проблему достаточно ясно и кратко, чтобы получить ценную помощь.
EDIT
Спасибо всем за подробные и полезные ответы. Как я и ожидал, есть несколько способов сделать это.
Я протестировал большинство ваших предложений (которые я понял) и загрузил тестовый проект в код Google для дальнейшего использования (проект maven)
http://code.google.com/p/javastats/
Я протестировал различные реализации моего StatsService
- HashMapStatsService (HMSS)
- ConcurrentHashMapStatsService (CHMSS)
- LinkedQueueStatsService (LQSS)
- GoogleStatsService (GSS)
- ExecutorConcurrentHashMapStatsService (ECHMSS)
- ExecutorHashMapStatsService (EHMSS)
и я проверил их с x
числом потоков, каждое из которых вызывало уведомление y
раз, результаты в мс
10,100 10,1000 10,5000 50,100 50,1000 50,5000 100,100 100,1000 100,5000
GSS 1 5 17 7 21 117 7 37 254 Summe: 466
ECHMSS 1 6 21 5 32 132 8 54 249 Summe: 508
HMSS 1 8 45 8 52 233 11 103 449 Summe: 910
EHMSS 1 5 24 7 31 113 8 67 235 Summe: 491
CHMSS 1 2 9 3 11 40 7 26 72 Summe: 171
LQSS 0 3 11 3 16 56 6 27 144 Summe: 266
В данный момент я думаю, что буду использовать ConcurrentHashMap, поскольку он предлагает хорошую производительность, в то время как его довольно легко понять.
Спасибо за ваш вклад!
Janning