Question

Checkstyle signale ce code comme étant "Le libellé du verrouillage à double contrôle est cassé", mais je ne pense pas que mon code soit affecté par les problèmes de verrouillage à double contrôle.

Le code est censé créer une ligne dans une base de données si une ligne avec cet identifiant n'existe pas. Il fonctionne dans un environnement multi-thread et je veux éviter les exceptions SQL de la clé primaire-existe.

Le pseudo-code:

private void createRow(int id) {
  Row row = dao().fetch(id);
  if (row == null) {
     synchronized (TestClass.class) {
        row = dao().fetch(id);
        if (row == null) {
           dao().create(id);
        }
     }
  }
}

Je peux convenir que cela ressemble à un verrouillage à double vérification, mais je n'utilise pas de variables statiques et le code dans fetch () et create () est probablement trop complexe pour être mis en ligne et mis hors service.

Est-ce que je me trompe ou checkstyle? :)

Était-ce utile?

La solution

En supposant que vous souhaitiez lire cette ligne la plus intérieure:

row = dao().create(id);

Ce n'est pas un problème de verrou classique à double vérification en supposant que dao (). fetch est correctement mutexé à partir de la méthode create.

Modifier : (le code a été mis à jour)

Le problème classique d'un verrou à double vérification est de se voir attribuer une valeur avant l'initialisation lorsque deux threads accèdent à la même valeur.

En supposant que le DAO soit correctement synchronisé et ne renvoie pas une valeur partiellement initialisée, cela ne présente pas les défauts de l'idiome de verrouillage à double vérification.

Autres conseils

Je pense que dans ce cas, checkstyle est correct. Dans votre code tel que présenté, considérez ce qui se produirait si deux threads avaient tous deux ligne == null à l'entrée du bloc synchronisé. Le fil A entrerait dans le bloc et insérerait la nouvelle ligne. Ensuite, après que le thread A ait quitté le bloc, le thread B entre dans le bloc (car il ne sait pas ce qui vient de se passer) et tente d'insérer à nouveau la même nouvelle ligne.

Je vois que vous venez de modifier le code et d’ajouter une ligne manquante assez importante. Dans le nouveau code, vous pourrez peut-être vous en sortir, puisque deux threads ne s'appuieront pas sur des modifications apportées à une variable partagée (statique). Mais vous feriez mieux de savoir si votre SGBD prend en charge une instruction telle que INSERT OR UPDATE .

Si vous devez déployer plusieurs serveurs d'applications, vous pouvez également déléguer cette fonctionnalité au SGBD. Étant donné que les blocs synchronized ne fonctionnent pas sur plusieurs ordinateurs, vous devrez faire autre chose dans ce cas.

Si vous êtes tenté d'écrire du code comme celui-ci, considérez:

  • Depuis Java 1.4, la synchronisation des méthodes est devenue relativement peu coûteuse. Ce n’est pas gratuit, mais le moteur d’exécution n’en souffre vraiment pas tant qu’il vaut la peine de risquer la corruption des données.

  • Depuis Java 1.5, vous avez les classes Atomic * qui vous permettent de lire et de définir des champs de manière atomique. Malheureusement, ils ne résolvent pas votre problème. Pourquoi ils n’ont pas ajouté AtomicCachedReference ou quelque chose du genre (qui appellerait une méthode pouvant être remplacée lorsque get () est appelé et que la valeur actuelle == null) me dépasse.

  • Essayez ehcache . Il vous permet de créer un cache (c’est-à-dire un objet qui vous permet d’appeler du code si une clé est pas dans une carte). C’est généralement ce que vous voulez et les caches résolvent vraiment votre problème (et tous les autres problèmes dont vous ne saviez même pas qu’ils existaient).

Comme d'autres l'ont souligné, ce code fera ce que vous souhaitez tel quel, mais uniquement sous un ensemble d'hypothèses non évidentes:

  1. Le code Java n'est pas en cluster (voir la réponse de @Greg H)
  2. La " rangée " la référence est seulement et que null est vérifié sur la première ligne, avant le bloc de synchronisation.

La raison pour laquelle l'idiome de verrouillage revérifié est rompu (conformément à la section 16.2.4 de la accès simultané à Java dans la pratique ) est qu’il est possible pour un thread exécutant cette méthode de voir une référence non-nulle mais mal initialisée à "ligne" avant de saisir le bloc synchronisé (à moins que "dao" fournisse une synchronisation correcte) . Si votre méthode faisait quoi que ce soit avec " rangée " autre que de vérifier qu'il est nul ou non, il serait cassé. Dans l’état actuel des choses, c’est probablement correct, mais très fragile - personnellement, je ne serais pas à l’aise à écrire ce code si j’imaginais qu’il y avait même une chance lointaine que d’autres développeurs puissent ultérieurement modifier la méthode. sans comprendre les subtilités de DCL.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top