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.
BEARBEITENVielen 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