concurrencia de Java: muchos escritores, un lector

Necesito recopilar algunas estadísticas en mi software y estoy tratando de hacerlo rápido y correcto, lo cual no es fácil (¡para mí!)

primero mi código hasta ahora con dos clases, un StatsService y un 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 es mi segunda clase, una cosechadora que recopila las estadísticas de vez en cuando y las escribe en una base de datos.

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);
}
}

En tiempo de ejecución tendrá aproximadamente 30 subprocesos simultáneos en ejecución cada llamadanotify(key)&nbsp;Cerca de 100 veces. Solo un StatsHarvester está llamandostatsService.getStats()

Entonces tengo muchos escritores y un solo lector. Sería bueno tener estadísticas precisas, pero no me importa si se pierden algunos registros en alta concurrencia.

El lector debe ejecutar cada 5 minutos o lo que sea razonable.

La escritura debe ser lo más rápida posible. La lectura debe ser rápida, pero si se bloquea durante unos 300 ms cada 5 minutos, está bien.

He leído muchos documentos (concurrencia de Java en la práctica, Java efectivo, etc.), pero tengo la fuerte sensación de que necesito su consejo para hacerlo bien.

Espero haber declarado mi problema lo suficientemente claro y breve como para obtener ayuda valiosa.

EDITAR

Gracias a todos por sus respuestas detalladas y útiles. Como esperaba, hay más de una forma de hacerlo.

Probé la mayoría de sus propuestas (las que entendí) y subí un proyecto de prueba al código de Google para obtener más referencias (proyecto maven)

http://code.google.com/p/javastats/

He probado diferentes implementaciones de mi StatsService

HashMapStatsService (HMSS)ConcurrentHashMapStatsService (CHMSS)LinkedQueueStatsService (LQSS)GoogleStatsService (GSS)ExecutorConcurrentHashMapStatsService (ECHMSS)ExecutorHashMapStatsService (EHMSS)

y los probé conx&nbsp;número de subprocesos que cada llamada notificay&nbsp;veces, los resultados están en 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

En este momento, creo que usaré ConcurrentHashMap, ya que ofrece un buen rendimiento y es bastante fácil de entender.

¡Gracias por todas sus aportaciones! Janning