J'ai deux méthodes dans ma classe, qui seront exécutées dans un environnement concurrent:
class Clazz { private int counter = 0; private volatile Map<..> map = new ConcurrentHashMap<>(); private int[] array; public void concurrentMethod() { ...perform some actions with map... } public int nonConcurrentMethod() { ...reinitialize the map...change references... counter++; return array[counter]; } }
La question est la suivante: en supposant que nonConcurrentMethod
doit être appelé par un seul thread à la fois, dois-je spécifier explicitement counter
et array
comme champs volatile
? Rendre le compteur atomique
?
Je pense qu'il vaudrait mieux s'assurer que tout fonctionne sans problème sur une vraie production.
3 Réponses :
Habituellement, c'est la classe entière qui a une sémantique spécifique thread-safe, par exemple HashMap
n'est pas thread-safe alors que ConcurrentHashMap
l'est. Ceci est particulièrement important si vous créez une bibliothèque, les gens peuvent contourner votre conception en appelant nonConcurrentMethod ()
à partir de plusieurs threads.
IMO si vous ne pouvez pas diviser Clazz code > en deux classes séparées avec une sémantique thread-safe différente, il serait prudent de rendre
nonConcurrentMethod ()
thread-safe. Dans le cas où nonConcurrentMetho ()
est appelé à partir de plusieurs threads, les performances se dégraderont mais l'exactitude sera conservée, en évitant les bogues difficiles à trouver.
Vous pouvez essayer un verrou interne, qui, espérons-le, ne sera pas trop coûteux en raison d'un verrouillage biaisé en l'optimisant lorsque le verrou est acquis à partir d'un seul thread:
private final Object lock = new Object(); public int nonConcurrentMethod() { synchronized(lock) { ...reinitialize the map...change references... counter++; return array[counter]; } }
Assurez-vous qu'au moins un des champs Clazz
est final
pour garantir publication sécurisée .
À partir des informations que vous avez fournies, les modifications individuelles de la map
seront vues par les utilisateurs de concurrentMethod
, mais les modifications apportées à l'intérieur d'un appel de méthode ne sont pas isolées d'un autre appel de méthode de toute autre méthode. Par conséquent, si nonConcurrentMethod
à un moment donné clear ()
est la carte, concurrentMethod
peut voir les entrées disparaître de la carte
entre les appels de méthodes sur la carte.
Par exemple:
10 0
Pourrait afficher:
concurrentMethod() { System.out.println(map.size()); System.out.println(map.size()); }
Si nonConcurrentMethod
efface la carte et est appelée pendant que concurrentMethod
est en cours d'exécution.
Si vous voulez assurer un accès atomique, alors plutôt que d'utiliser ConcurrentHashMap code >, essayez d'utiliser un
HashMap
normal et de le protéger en protégeant les méthodes avec le mot-clé synchronized
ou en utilisant un verrouillage plus granulaire, soit via synchronized (lock) {}
ou en utilisant certains des outils de verrouillage fournis dans le package d'accès concurrentiel Java, tels que l'interface ReadWriteLock
.
Il est préférable de déplacer les objets partagés dans une classe différente accessible par plusieurs threads.
Cela peut créer de sérieux problèmes à plus long terme en raison d'un tel désordre et il sera difficile de trouver la cause fondamentale pour les développeurs qui n'ont jamais travaillé sur ce module.
Essayez de rendre le code propre dans un tel manière qu'il n'y a aucune possibilité de bogue caché ou d'hypothèse dans l'implémentation du code.
Même toutes les méthodes de ConcurrentHashMap
sont thread-safe, nous devons encore assurer la sécurité des threads lorsque différentes méthodes thread-safe sont appelées par plusieurs threads.
Par exemple:
Map map = new ConcurrentHashMap(); Thread t1 = new Thread(); Thread t2 = new Thread(); public void putIfAbsent(String key, String value) { if (!map.containsKey(key)) { map.put(key, value); } }
Dans l'exemple ci-dessus, nous avons partagé la ressource map
et deux threads mettent des valeurs en cas d'absence.
Pour éviter une telle situation, nous pouvons utiliser deux techniques comme mentionné ci-dessous;
Je pense qu'il n'est pas nécessaire de rendre votre carte aussi volatile. ConcurrentHashMap en lui-même est thread-safe, mieux vaut le rendre final.
@MukeshVerma et si je change la référence et réinitialise cette carte dans
nonConcurrentMethod
? Tous les autres threads utilisant activementconcurrentMethod
devraient voir le champ mis à jourpuisque vous l'avez marqué comme final, vous ne pouvez pas vraiment réaffecter la référence.
@MukeshVerma mon mauvais, la description est corrigée
Si vous êtes si sûr que votre fonction nonConcurrent ne sera pas appelée par plus d'un thread à la fois, je pense que vous n'avez rien d'autre à faire.Faire des choses comme atomiques, volatiles ou marquer votre fonction comme synchronisée aura un frais généraux, évitez si vous n'en avez pas besoin.
Peu importe que
nonConcurrentMethod ()
soit «appelé par un seul thread à la fois», il nécessite des constructions thread-safe pour assurer la visibilité des modifications apportées lors d'un appel à l'appel suivant par un autre fil. Mais quel est le problème de rendre la méthodesynchronisée
alors qu’elle est censée être «appelée par un seul thread à la fois» de toute façon?