Java-Parallelität: viele Autoren, ein Leser

Ich muss einige Statistiken in meiner Software sammeln und ich versuche, sie schnell und korrekt zu machen, was nicht einfach ist (für mich!)

Zuerst mein Code bisher mit zwei Klassen, einem StatsService und einem 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;
}
}

Dies ist meine zweite Klasse, ein Mähdrescher, der von Zeit zu Zeit die Statistiken sammelt und sie in eine Datenbank schreibt.

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

Zur Laufzeit werden ca. 30 Threads gleichzeitig ausgeführtnotify(key) ungefähr 100 mal. Nur ein StatsHarvester ruft anstatsService.getStats()

Ich habe also viele Schriftsteller und nur einen Leser. Es wäre schön, genaue Statistiken zu haben, aber es ist mir egal, ob einige Datensätze bei hoher Parallelität verloren gehen.

Der Reader sollte alle 5 Minuten laufen oder was auch immer zumutbar ist.

Das Schreiben sollte so schnell wie möglich sein. Das Ablesen sollte schnell sein, aber wenn es alle 5 Minuten für etwa 300 ms blockiert, ist es in Ordnung.

Ich habe viele Dokumente gelesen (Java-Parallelität in der Praxis, effektives Java usw.), aber ich habe das starke Gefühl, dass ich Ihren Rat brauche, um es richtig zu machen.

Ich hoffe, ich habe mein Problem klar und deutlich genug formuliert, um wertvolle Hilfe zu erhalten.

BEARBEITEN

Vielen Dank an alle für Ihre detaillierten und hilfreichen Antworten. Wie ich erwartet hatte, gibt es mehr als einen Weg, dies zu tun.

Ich habe die meisten Ihrer Vorschläge getestet (die ich verstanden habe) und ein Testprojekt zur weiteren Bezugnahme auf Google-Code hochgeladen (Maven-Projekt).

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

Ich habe verschiedene Implementierungen meines StatsService getestet

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

und ich habe sie mit getestetx Anzahl der Threads, die jeder Anruf benachrichtigty mal sind ergebnisse in 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

In diesem Moment denke ich, dass ich ConcurrentHashMap verwenden werde, da es eine gute Leistung bietet, während es ziemlich einfach zu verstehen ist.

Vielen Dank für all Ihre Beiträge! Janning

Antworten auf die Frage(9)

Ihre Antwort auf die Frage