Utilisez ConcurrentHashMap. Elle verrouille par bin plutôt que la map entière, avec un compare-and-set pour les bins vides et une synchronisation sur la tête du bin sinon, si bien que les lecteurs ne bloquent jamais et que les écritures dans des bins différents avancent en parallèle. Une map synchronisée sérialise chaque opération sur un seul verrou, et elle exige quand même une synchronisation externe pendant l'itération. Utilisez ses méthodes atomiques, comme compute et merge, plutôt qu'un get suivi d'un put.
Pourquoi les recruteurs posent cette question
Cela vérifie si vous savez pourquoi les collections concurrentes existent, au lieu de dégainer synchronized par réflexe. Le recruteur veut la différence de granularité et le point pratique crucial : envelopper une map ne rend pas atomiques les séquences d'opérations. Savoir que size et l'itération sont faiblement cohérentes, et quand CopyOnWriteArrayList ou une BlockingQueue est le meilleur outil, complète la réponse.
Comment structurer votre réponse
- Nommez l'outil d'abord, puis le mécanisme qui le rend plus rapide.
- Expliquez pourquoi un wrapper synchronisé ne suffit toujours pas pour les actions composées.
- Pointez les méthodes atomiques pour la lecture-modification-écriture.
- Mentionnez les autres collections concurrentes et quand elles conviennent.
Exemple de réponse
ConcurrentHashMap, presque toujours. La différence importante est la granularité : elle verrouille au niveau d'un seul bin, et une insertion dans un bin vide n'est qu'un compare-and-set, donc des clés sans rapport n'entrent pas en contention et les lectures sont sans verrou. Une map synchronisée met un verrou unique autour de tout, donc elle devient le goulet dès que plusieurs threads travaillent. Le plus gros piège avec un wrapper synchronisé, c'est que les appels individuels sont atomiques mais pas les séquences : si je teste containsKey puis que je fais put, un autre thread peut se glisser entre les deux, et l'itération n'est pas sûre du tout si je ne tiens pas le verrou moi-même. C'est pour ça que j'utilise les méthodes atomiques : computeIfAbsent pour une entrée de cache construite paresseusement, merge pour des compteurs, comme ça la lecture-modification-écriture se passe à l'intérieur de la map. Les choses à savoir : size est une estimation en cas de modification concurrente et les itérateurs sont faiblement cohérents, donc ils ne lèvent pas d'exception mais peuvent ne pas voir les écritures les plus récentes. Pour d'autres formes j'utilise CopyOnWriteArrayList quand les lectures dépassent largement les écritures, et une BlockingQueue pour le passage de main entre producteur et consommateur.
Vous passez cet entretien bientôt ? GhostPilot écoute votre appel en direct, repère la question dès qu'elle est posée et affiche une réponse structurée à l'écran en temps réel. Essayez-le lors de votre prochain entretien blanc, ou prenez un Session Pass à $29, sans abonnement, pour le jour J.
Voir comment ça marcheQuestions de relance à prévoir
- Quels sont les risques à faire un traitement coûteux à l'intérieur de computeIfAbsent ?
- Que signifie une itération faiblement cohérente en pratique ?
- Quand CopyOnWriteArrayList est-elle le mauvais choix ?
Autres questions pour Développeur Java
Votre recruteur posera sa propre version de celle-ci. Collez votre véritable fiche de poste dans le Question Predictor gratuit et obtenez les 20 questions que ce poste a le plus de chances de poser, avec ce que chacune cherche vraiment à sonder.
Prédire mes questions