Bonjour,
Comme le titre le suppose, je travaille actuellement sur un batch qui traite plusieurs millions d'objets en parallèle. J'aimerais que chacun d'entre eux puisse incrémenter différents compteurs (nb d'objets traités, nb d'erreurs de type 1, nb d'erreurs de type 2 etc.), et par conséquent de manière synchronisée.
Je ne suis que novice en matière d'environnement multithread
Je me suis donc orienté vers une classe qui utilise des Tables de hachage.
Deux tables de hachage : l'une pour les compteurs, l'autre pour leur description, chacune identifiant les compteurs par une clé unique. Elles sont locales à une instance singleton de ma classe Compteur, initialisées par le ClassLoader.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 /** * Maintient des compteurs et fournit des méthodes pour * les incrémenter et les décrire. * */ public final class Compteur { /** Logger */ private static final Logger LOGGER = Logger.getLogger(Compteur.class); /** L'instance singleton de Compteur */ private static final Compteur SINGLETON = new Compteur(); /** La table des compteurs */ private final HashMap<String, Integer> mCompteurs; /** La table des descriptions de compteurs */ private final HashMap<String, String> mDescriptions; /** * Constructeur privé */ private Compteur(){ super(); mCompteurs = new HashMap<String, Integer>(); mDescriptions = new HashMap<String, String>(); } public static synchronized void increment(final String pNomCompteur){ Integer lCount = SINGLETON.mCompteurs.get(pNomCompteur); if (lCount == null){ SINGLETON.mCompteurs.put(pNomCompteur, 1); } else { SINGLETON.mCompteurs.put(pNomCompteur, lCount++); } } }
J'aimerais porter votre attention sur la méthode d'incrémentation. Celle-ci s'effectue en deux temps : d'abord je récupère la valeur du compteur identifié par sa clé, puis je l'incrémente et remplace sa valeur dans la table des compteurs.
Je me suis orienté vers 2 implémentations, chacune soulevant des questions que j'aimerais vous soumettre :
- celle que j'ai publiée ci dessus, créer la table des compteurs à l'aide de new HashMap<String, Integer>(), donc sans synchronisation intrinsèque. La méthode increment() est donc synchronized. Les questions ici sont donc donc :
- est-ce une bonne approche ?
- étant donné que je parcours des millions d'objets, l'utilisation coûteuse de sychronized ne risque-t-elle pas de dégrader fortement les performances du batch ?
- une deuxième implémentation serait d'utiliser une ConcurrentHashMap pour la table des compteurs, ce qui synchroniserait la méthode d'écriture (SINGLETON.mCompteurs.replace(pNomCompteur, lCount++)). La méthode d'incrément ne serait donc plus synchronized. Malheureusement, l'accès à la valeur précédente du compteur ne serait pas synchronisée et donc je risquerais de me trouver avec plusieurs threads qui modifient la valeur du compteur sans prendre en compte sa nouvelle valeur. Je pencherais pour l'utilisation de volatile pour la table des compteurs, mais je ne suis pas sûr d'avoir compris son sens. Les questions ici sont donc donc :
- l'utilisation de volatile est-elle justifiée ici ?
- cette implémentation est-elle moins coûteuse que la précédente ?
Je vous remercie d'avance pour vos analyse, et si vous avez de meilleures solutions à me proposer n'hésitez pas à m'en faire part.



Répondre avec citation




Partager