3 votes

Des threads distincts, accédant aux mêmes méthodes synchronisées, renvoient des résultats inattendus.

J'ai du mal à comprendre comment la synchronisation fonctionne sur deux méthodes distinctes du même objet. J'ai une classe, avec deux méthodes d'instance qui sont déclarées synchronisées. Trois threads accèdent aux deux méthodes synchronisées de cet objet, mais le résultat est inattendu. Les threads accèdent indifféremment aux deux méthodes. Ils n'attendent pas que le verrou de l'objet entier soit libéré. Voici l'exemple :

public class ThreadSafeCounterSameMonitor2 {

    private int value;

    public synchronized int getValue() {
        return this.value;
    }

    public synchronized void setValue(int value) {
        this.value = value;
    }

    public static void main(String[] args) {

        ThreadSafeCounterSameMonitor2 nts = new ThreadSafeCounterSameMonitor2();

        Thread th1 = new Thread(new Runnable() {
            public void run() {
                nts.setValue(5);
                System.out.println("Thread Id " + Thread.currentThread().getId() + ", expected value is 5, value=" + nts.getValue());
            }
        });

        Thread th2 = new Thread(new Runnable() {
            public void run() {
                nts.setValue(10);
                System.out.println("Thread Id " + Thread.currentThread().getId() + ", expected value is 10, value="
                        + nts.getValue());
            }
        });

        Thread th3 = new Thread(new Runnable() {
            public void run() {
                nts.setValue(15);
                System.out.println("Thread Id " + Thread.currentThread().getId() + ", expected value is 15, value="
                        + nts.getValue());
            }
        });

        th1.start();
        th2.start();
        th3.start();
    }

}

Le résultat que j'obtiens est surprenant :

Thread Id 13, expected value is 15, value=15.  
Thread Id 12, expected value is 10, value=15.  
Thread Id 11, expected value is 5, value=15.  

Donc, là où je m'attendais à 10, il y en a 15. Là où j'attendais 5, j'obtiens encore 15.

J'avais cru comprendre que toutes les méthodes synchronisées du moniteur seraient verrouillées en cas d'accès par un thread. Mais elles ne le sont pas. Dans la même méthode run() d'un thread, je définis une valeur, puis je la récupère et je reçois une autre valeur, modifiée par un autre thread.

J'ai souvent lu quelque chose comme "Une méthode synchronisée acquiert un verrou implicite sur cette référence", mais ce n'est clairement pas vrai, pas même pour toutes les méthodes synchronisées dans le même moniteur. Ou est-ce que je fais quelque chose de mal dans le code ?

Ma question est la suivante : comment puis-je vraiment verrouiller l'ensemble des méthodes synchronisées de l'objet ? Connaissez-vous un tutoriel qui explique bien ce concept ?

5voto

Ivan Points 6349

Il n'y a pas de résultats imprévisibles ici. Et la synchronisation fonctionne correctement. Le problème est que vous appelez la méthode setValue() et ensuite, après un certain temps, vous appelez getValue() . Dans votre exemple, les threads se superposent de telle manière que la valeur 15 est définie après que les autres threads appellent setValue() mais avant que l'un d'entre eux n'appelle getValue() .

Vous pouvez ajouter Thread.sleep(1000) en el run() méthode du troisième fil et voyez que les résultats peuvent changer.

La méthode synchronisée acquiert un verrou explicite sur l'objet mais seulement pour durée de cette méthode . Une fois la méthode terminée, le verrou est rendu et les autres threads sont libres de le prendre.

2voto

jWeaver Points 4191

Vous avez créé 3 fils et vous avez commencé, donc, j'espère que vous êtes conscient que l'exécution des fils n'est pas garantie immédiatement. Il est possible que le thread que vous avez créé et démarré puisse commencer à fonctionner à la fin, ce qui se produit dans votre cas. Mais, ce n'est pas toujours garanti. Par exemple, le même code que j'ai exécuté et qui donne la sortie attendue sur Idéone

Thread Id 8, expected value is 5, value=5
Thread Id 9, expected value is 10, value=10
Thread Id 10, expected value is 15, value=15

Mais, si vous vouliez exécuter les fils l'un après l'autre, alors vous pourriez regarder rejoindre méthode.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X