simultaneidade java: muitos escritores, um leitor
Preciso reunir algumas estatísticas no meu software e estou tentando torná-lo rápido e correto, o que não é fácil (para mim!)
primeiro meu código até agora com duas classes, um StatsService e um 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;
}
}
esta é minha segunda classe, uma colheitadeira que coleta as estatísticas de tempos em tempos e as grava em um banco de dados.
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);
}
}
Em tempo de execução, ele terá cerca de 30 threads simultâneos em execução cada chamadanotify(key)
cerca de 100 vezes. Apenas um StatsHarvester está chamandostatsService.getStats()
Então, eu tenho muitos escritores e apenas um leitor. seria bom ter estatísticas precisas, mas não me importo se alguns registros forem perdidos em alta simultaneidade.
O leitor deve executar a cada 5 minutos ou o que for razoável.
Escrever deve ser o mais rápido possível. A leitura deve ser rápida, mas se travar por cerca de 300 ms a cada 5 minutos, tudo bem.
Eu já li muitos documentos (concorrência Java na prática, java eficaz e assim por diante), mas tenho a forte sensação de que preciso do seu conselho para fazer a coisa certa.
Espero ter declarado meu problema claro e curto o suficiente para obter ajuda valiosa.
EDITARObrigado a todos por suas respostas detalhadas e úteis. Como eu esperava, há mais de uma maneira de fazê-lo.
Testei a maioria das suas propostas (aquelas que eu entendi) e enviei um projeto de teste para o google code para obter mais referências (projeto maven)
http://code.google.com/p/javastats/
Testei diferentes implementações do meu StatsService
HashMapStatsService (HMSS)ConcurrentHashMapStatsService (CHMSS)LinkedQueueStatsService (LQSS)GoogleStatsService (GSS)ExecutorConcurrentHashMapStatsService (ECHMSS)ExecutorHashMapStatsService (EHMSS)e eu os testei comx
número de threads que cada chamada notificay
vezes, os resultados estão em 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
Neste momento, acho que vou usar o ConcurrentHashMap, pois oferece um bom desempenho e é muito fácil de entender.
Obrigado por toda a sua contribuição! Janning