Supposons que nous ayons une classe appelée comptables qui gère l'état des comptes.
comptable est défini comme p> étant donné cette définition, quelle est la meilleure façon de mettre en œuvre Transfert () afin que vous puissiez garantir que le transfert est une opération atomique. P> Je suis intéressé par des réponses qui référence Java 1.4 Code ainsi que des réponses pouvant utiliser des ressources de Java.Util.ConCurrent à Java 5 p> p>
5 Réponses :
Synchroniser sur les deux code> objets code> et faire le transfert. Assurez-vous de toujours synchroniser dans le même ordre. Pour ce faire, apportez le Si vous ne commandez pas les comptes, vous exécutez la possibilité d'une impasse si un thread se transfère de A à B et un autre transfert de B à A. P>
Cet exemple exact est discuté à la page 207 de Java Concurrence dans la pratique , un livre critique pour quelqu'un qui fait plusieurs Développement java enfilé. Le code d'exemple est disponible auprès du site Web de l'éditeur : p>
Compte Code> S Implémente
comparable code>, trier les deux comptes et synchroniser dans cet ordre. P>
+1: Verrouillage toujours les objets dans le même ordre ne peut pas être excédité.
Si la performance n'est pas critique, et il existe un seul comptable, on pourrait aussi simplement synchroniser code> la méthode de transfert.
Sur une note latérale, si vous traitez avec Java 5 et plus récent, il peut être préférable de mettre en œuvre comparable
comparable code>.
Aucun de cette synchronisation ne garantit que si une moitié de la transaction échoue, l'autre moitié échouera. Cela traite simplement du problème de synchronisation.
Veuillez citer le site des auteurs JCIP.NET , qui relie des copies plus lisibles du code exemple, plutôt que du plagiaire .
@trashgod: Merci de pointer cela. J'ai mis à jour ma réponse.
Vous devez probablement avoir une assistance complète des transactions (s'il s'agit d'une application réelle bien sûr). P>
La difficulté de la solution dépend à peine de votre environnement. Décrivez votre système en détail et nous essaierons de vous aider (quel type d'application? Utilise-t-il Web-serveur? Quel serveur Web? Qu'est-ce qui est utilisé pour stocker des données? Etc.) p>
+1 pour traiter les problèmes de non-synchronisation avec les transactions atomiques.
Un exemple classique très bien expliqué ici -
ne pouviez-vous pas éviter de se synchroniser à l'aide d'un atomicreference
get () code> et
défini () code>? p>
Vous devez toujours synchroniser sur les accès sur ce atomicreference
bal.set (BAL.GET () + créditamt) code> n'est pas atomique: quelque chose pourrait changer la référence détenue par BAL entrant entre l'appel à
get () code> et
définir () code>. Mais il s'agit davantage d'attacher les deux opérations (débit et crédit) dans une opération atomique, surtout de manière à ce que l'autre échoue, l'autre échouera également (Rollback).
S'assurer que je comprends bien ... Le problème potentiel utilisant des variables atomiques sans synchronisation n'est pas ce que ma femme fait (change la valeur du bal entre mes appels pour obtenir () et définir ()), mais aussi ce que fait mon ex-femme (Modification de la référence à BAL entre mes appels pour obtenir () et définir ())?
"Valeur" et "référence" sont difficiles à utiliser dans ce contexte car vous avez une référence ( atomicreference - BAL code>) à une référence (
double - bal.value code>) à un double valeur (
double - bal.value.value code>). La référence
BAL code> serait définitive, donc ce n'est pas un problème. Le problème de synchronisation vient en train de faire les mathématiques, où vous avez besoin pour le premier 1)
obtenir () code> Le double de l'atomreference, 2) faire des mathématiques sur elle et 3)
SET () code> la valeur en arrière. Si vous ne faites pas de 1-2-3 atomique, vous pourriez avoir un fil de fil A 1 et 2, puis le fil b Faites 1, puis le filetage A DO 3, puis le thread b do 2 et 3 tuant les changements d'A.
Si vous pouvez vous garantir que tous les accès sont effectués via la méthode de transfert, l'approche la plus facile est probablement simplement pour transférer une méthode synchronisée. Ceci sera le fil-coffre-fort car cela garantit qu'un seul thread exécutera la méthode de transfert à la fois. P>
Si d'autres méthodes peuvent également accéder à la comptabilité, vous pouvez décider de les utiliser tous d'utiliser une seule serrure globale. Un moyen facile de faire est de surmonter tout le code qui accède à la comptabilité dans un bloc synchronisé (x) {...} où X est une instance d'objet partagée / singleton (qui pourrait être l'instance ComptableVice elle-même). Ce fil sera en sécurité car un seul fil accédera à la comptabilité à une heure, même si elles sont différentes. P>
Si cela n'est toujours pas suffisant, vous devrez alors utiliser des approches de verrouillage plus sophistiquées. Une approche commune serait de verrouiller les comptes individuellement avant de les modifier ... mais vous devez alors faire très attention à prendre les verrous dans un ordre cohérent (par exemple par ID de compte) sinon vous passerez dans des blocages. P>
Enfin, si comptable est un service distant, vous êtes dans un territoire de verrouillage distribué ... si vous avez un doctorat en informatique et des années de budget de recherche pour vous brûler, vous devriez probablement éviter d'y aller. P>
P.s. Pour la simplicité et la généralité, j'irais pour la serrure globale jusqu'à ce que vous appuyiez sur une question de performance / d'évolutivité (ce qui peut ne jamais être!) Et seulement alors commencer à penser à passer au verrouillage au niveau du compte.
J'apprécie la complétude de votre réponse. Ce que vous avez mentionné dans votre commentaire est précisément où je me dirigeais. Une fois que vous devez entrer en compte le verrouillage de niveau, il semble qu'il existe un système plus élaboré que de simplement utiliser le mot-clé «synchronisé».
@Danielhonig: Il est vrai qu'il est bon d'éviter une optimisation prématurée, mais dans de nombreuses systèmes de traitement de la transaction (où le travail de la machine est essentiellement juste de le faire toute la journée sur des millions d'enregistrements), le concepteur ne tenterait jamais d'utiliser un MuTex global. Ils sont souvent conçus avec de nombreuses noyaux (beaucoup!) Et essayer de travailler en parallèle autant que possible. Il est donc bon de savoir pourquoi le verrouillage basé sur un compte est utile dans ce domaine.