Мне нужно собрать некоторые статистические данные в моем программном обеспечении, и я пытаюсь сделать это быстро и правильно, что нелегко (для меня!)
сначала мой код до сих пор с двумя классами, 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 раз. Только один статистический прибор вызывает statsService.getStats()
Итак, у меня много писателей и только один читатель. было бы неплохо иметь точную статистику, но мне все равно, если некоторые записи будут потеряны на высоком уровне concurrency.
Читатель должен работать каждые 5 минут или что-то разумное.
Письмо должно быть как можно быстрее. Чтение должно быть быстрым, но если он запирает около 300 мс каждые 5 минут, это прекрасно.
Я прочитал много документов (Java concurrency на практике, эффективная Java и т.д.), но у меня есть сильное чувство, что мне нужен ваш совет, чтобы понять это.
Надеюсь, я сказал, что моя проблема ясна и достаточно короткая, чтобы получить ценную помощь.
ИЗМЕНИТЬ
Спасибо всем за подробные и полезные ответы. Как я и ожидал, есть несколько способов сделать это.
Я проверил большинство ваших предложений (я понял) и загрузил тестовый проект в код google для дальнейшей справки (проект maven)
http://code.google.com/p/javastats/
Я тестировал различные реализации моей программы StatsService
- HashMapStatsService (HMSS)
- ConcurrentHashMapStatsService (CHMSS)
- LinkedQueueStatsService (LQSS)
- Служба GoogleStatsService (GSS)
- ExecutorConcurrentHashMapStatsService (ECHMSS)
- ExecutorHashMapStatsService (EHMSS)
и я проверил их с числом x
числа потоков, каждый из которых уведомляет y
раз, результаты находятся в ms
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