4
votes

Définition de champs dans un environnement simultané

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.


6 commentaires

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 activement concurrentMethod devraient voir le champ mis à jour


puisque 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éthode synchronisée alors qu’elle est censée être «appelée par un seul thread à la fois» de toute façon?


3 Réponses :


1
votes

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 .


0 commentaires

0
votes

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


0 commentaires

0
votes

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;

  • Soit chaque appelant s'assurera de le rendre thread-safe (côté client)
  • Ou définissez une nouvelle méthode à un endroit pour garantir la sécurité des threads (côté serveur préférable )


0 commentaires