Parfois, en environnement multithread, il peut être tentant d’utiliser des structures de données qui ne sont pas thread-safe. Je pense notamment aux Map utilisées comme cache, pour lesquelles on pourrait être tenté de se dire :
1. Ce n’est pas grave si la donnée est écrite par plusieurs threads différents dans la Map, étant donné que le couple clef/valeur sera le même.
2. La synchronisation coûte cher, donc autant éviter de l’utiliser. Le problème de cette approche est qu’en fait, les structures de données thread-safe ne le sont pas pour vos données, et ne le sont pas non plus au niveau de leur état interne.
Débloque les + belles offres tech en 10 mins
Cela signifie tout simplement que leur état interne peut se corrompre et donner des effets de bord assez… indésirables.
Par exemple, si vous utilisez une HashMap en multithread, vous risquez d’obtenir des boucles infinies, tel que détaillé dans ce rapport de bug. A titre personnel, j’ai déjà fait monter le load average d’un serveur quad core à 42 pour une HashMap utilisée dans un filtre http en lieu et place de son équivalent thread-safe.
Alors bien évidemment on peut se dire qu’il suffit de mettre un bloc synchronized autour de chaque appel aux structures concernées, mais chacun de ces blocs implique une barrière en lecture/écriture qui est extrêmement coûteuse. Les dernières versions de la JVM savent heureusement ignorer les blocs synchronized quand l’objet n’est pas utilisé en contexte multithread. Mais quand même !
Pour éviter de telles mésaventures, voici une liste non exhaustive de structures de données à utiliser sur des objets dont les instances peuvent être partagées entre plusieurs threads :
Structure non thread-safe | Equivalent thread-safe | Commentaires |
java.util.HashMap et java.util.HashSet | java.util.concurrent.ConcurrentHashMap java.util.Hashtable | Ces structures n’acceptent ni clef, ni valeur nulle. La ConcurrentHashMap est nettement plus rapide que la Hashtable. Le HashSet utilise en fait une HashMap de manière sous-jacente, l’astuce consiste donc à utiliser les Map thread-safe avec la valeur égale à la clef. |
java.util.TreeMap et java.util.TreeSet | java.util.concurrent.ConcurrentSkipListMap | Le TreeSet utilise en fait une TreeMap de manière sous-jacente. L’astuce consiste donc à utiliser les Map thread-safe avec la valeur égale à la clef. |
java.util.ArrayList et java.util.LinkedList | java.util.Vector | Alors oui Vector date, mais il est thread-safe contrairement à ArrayList. Cela évitera les mauvaises surprises lors du redimensionnement du tableau sous-jacent à la liste. |
Débloque les + belles offres tech en 10 mins
Cet article vous a plu ? Vous aimerez sûrement aussi :
Julien
Moi c’est Julien, ingénieur en informatique avec quelques années d’expérience. Je suis tombé dans la marmite étant petit, mon père avait acheté un Apple – avant même ma naissance (oui ça date !). Et maintenant je me passionne essentiellement pour tout ce qui est du monde Java et du système, les OS open source en particulier.
Au quotidien, je suis devops, bref je fais du dév, je discute avec les opérationnels, et je fais du conseil auprès des clients.