IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Java Discussion :

Erreur d'implantation EventListenerList dans JDK?


Sujet :

Java

  1. #1
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut Erreur d'implantation EventListenerList dans JDK?
    D'après moi, en regardant le code source de EventListenerList, il y a une erreur (ou plutôt un cas non géré) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public synchronized <T extends EventListener> void add(Class<T> t, T l) {
        // c'est synchronized
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    public synchronized <T extends EventListener> void remove(Class<T> t, T l) {
        // c'est synchronized
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // non synchronized
    public <T extends EventListener> T[] getListeners(Class<T> t) {
        Object[] lList = listenerList; 
        int n = getListenerCount(lList, t); 
        T[] result = (T[])Array.newInstance(t, n); 
        int j = 0; 
        for (int i = lList.length-2; i>=0; i-=2) {
            if (lList[i] == t) {
    	    result[j++] = (T)lList[i+1];
            }
        }
        return result;   
    }
    Le problème se trouve sur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object[] lList = listenerList;
    En effet, sur une machine MULTI-processeurs, un processeur P1 peut faire un getListeners, puis un autre P2 peut faire un add, suivi plus tard par un getListeners par le processeur P1.
    Le 2e getListeners de P1 peut potentiellement ne pas voir la modification effectuée par le add de P2, même si le getListeners a lieu bien après (même si en pratique la probabilité est très faible). Ceci se produit si le cache de P1 n'a pas été invalidé (du moins pour la variable listenerList), ce qui est possible vu que la méthode getListeners n'est pas synchronisée et que listenerList n'est pas volatile.

    Même si on peut considérer que getListeners est faite en théorie pour être exécutée dans l'EDT, les add et remove peuvent être appelés de l'extérieur (sinon ils ne seraient pas synchronized).

    On pourrait certes considérer que le niveau de cohérence ne soit pas la "cohérence séquentielle", et que les lectures peuvent être anciennes, mais ça serait une hypothèse un peu trop faible...

    Je peux me tromper, mais ça ne serait pas la première fois que la synchronisation n'est correcte dans java (avant java 1.5, volatile ne marchait pas, et les moniteurs ne pouvaient avoir qu'une variable condition).

    Qu'en pensez-vous?

  2. #2
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Points : 635
    Points
    635
    Par défaut
    Ici le but du synchronized c'est de ne pas pouvoir faire un add et un remove en même temps, c'est tout.
    Ce n'est pas forcément utile de garantir qu'un appel à une méthode voit systématiquement les modifications faites par les appels précédents (ce que tu appelles "cohérence séquentielle"). Dans ton exemple, le fait que P1 ne voit pas la modif de P2 n'a pas vraiment d'importante, de toute façon s'il avait commencé un peu plus tôt la modif n'avait même pas commencé.
    Ici le plus important c'est que que si deux add (ou deux remove, ou un add et un remove) se font "en même temps", les opérations correspondantes sont bien effectuées. Sans le synchronized tu peux par exemple avoir un add qui écrase la modification d'un autre add.

  3. #3
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par the-gtm
    Ici le but du synchronized c'est de ne pas pouvoir faire un add et un remove en même temps, c'est tout.
    Certes, mais en plus ça met en cohérence les caches.

    Citation Envoyé par the-gtm
    Ce n'est pas forcément utile de garantir qu'un appel à une méthode voit systématiquement les modifications faites par les appels précédents (ce que tu appelles "cohérence séquentielle"). Dans ton exemple, le fait que P1 ne voit pas la modif de P2 n'a pas vraiment d'importante, de toute façon s'il avait commencé un peu plus tôt la modif n'avait même pas commencé.
    Mais ce qui est génant, c'est par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Processeur 1
    ------------
    (1) a = 3
    (2) getListeners()
    (3) println(a)
    (4) getListeners()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Processeur 2
    ------------
    (5) addListener(...)
    (6) a = 6
    Si l'ordre d'exécution est 1 2 5 6 3 4, la ligne 3 affiche bien 6, mais le getListeners() de la ligne 4 peut ne pas voir l'ajout fait par P2, ce qui ici est incohérent (au sens de la cohérence séquentielle) car l'ajout est effectué avant l'affectation de 6 à a...

    Citation Envoyé par the-gtm
    Ici le plus important c'est que que si deux add (ou deux remove, ou un add et un remove) se font "en même temps", les opérations correspondantes sont bien effectuées. Sans le synchronized tu peux par exemple avoir un add qui écrase la modification d'un autre add.
    Ça on n'est d'accord, les synchronized de add et remove sont nécessaires... mais pas suffisants sur un multiprocesseur...

  4. #4
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Points : 635
    Points
    635
    Par défaut
    Le problème dans dans exemple est que tu as deux objets : "a" et la ListenerList que tu veux garder cohérents entre eux (si addListener a été appelé alors a = 6, sinon a = 3).
    Pour ça la seule solution possible est de créer un verrou qui englobe les opérations d'écritures et de lecture sur ces deux variables.

    Cela dit il n'y a jamais eu de contrat de "cohérence séquentielle" dans Java alors je ne vois pas où est le problème. Ce n'est pas parce qu'une méthode commence avant une autre qu'elle doit finir avant aussi.

  5. #5
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par the-gtm
    Le problème dans dans exemple est que tu as deux objets : "a" et la ListenerList que tu veux garder cohérents entre eux (si addListener a été appelé alors a = 6, sinon a = 3).
    Pour ça la seule solution possible est de créer un verrou qui englobe les opérations d'écritures et de lecture sur ces deux variables.

    Cela dit il n'y a jamais eu de contrat de "cohérence séquentielle" dans Java alors je ne vois pas où est le problème. Ce n'est pas parce qu'une méthode commence avant une autre qu'elle doit finir avant aussi.
    Ici le problème n'est pas "une méthode commence avant une autre doit finir avant"... (heureusement que ça n'est pas vrai)

    C'est l'ordre d'exécution au sein d'un seul processeur...
    En gros, au départ a = 0 et b = 0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    P2:
    print(b)
    print(a)
    résultat :
    b = 2
    a = 0

    (EDIT: l'ordre des print est important pour montrer le problème)

    C'est incohérent séquentiellement car si b = 2, la ligne a = 1 a forcément été exécutée.


    Pour reprendre le problème avec les listeners sur un multiprocesseur, voici ce qui pourrait se produire (avec une très faible proba, je te l'accorde):
    - P2 fait un addListener(listener).
    - 5 minutes après, P1 fait un getListeners(), et ne voit pas le listener ajouté par P2, pour P1 getListeners() renvoie toujours un tableau vide.

    Et ça, c'est génant...



    EDIT: Le niveau de cohérence garanti dans ce cas, si je ne me trompe pas, est REPEATABLE_READ (lectures fantômes acceptées), ce qui est un peu faible quand même...

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Points : 635
    Points
    635
    Par défaut
    Citation Envoyé par ®om
    C'est l'ordre d'exécution au sein d'un seul processeur...
    En gros, au départ a = 0 et b = 0 :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    P2:
    print(a)
    print(b)
    résultat :
    a = 0
    b = 2

    C'est incohérent séquentiellement car si b = 2, la ligne a = 1 a forcément été exécutée.

    Ton exemple c'est toujours la même chose : tu as deux variables et tu veux soit (a,b) = (0, 0), soit (a, b) = (1, 2), il faut donc mettre un verrou autour des ecritures lectures.

    Tu dis que si P2 affiche 0 puis 2 alors "C'est incohérent séquentiellement car si b = 2, la ligne a = 1 a forcément été exécutée."
    Mais c'est bien le cas ! seulement au moment ou P2 a affiché a, on était dans l'état intermédiaire (a, b) = (0, 2)

    Pour eviter ça on met un verrou :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    P1:
    synchronized(verrou) {
      a = 0
      b = 2
    }
     
    P2 :
    synchronized(verrou) {
      print(a)
      print(b)
    }
    Et cela dit, pas besoin d'une machine multiprocesseur pour avoir des problèmes de concurrence, il suffit d'avoir plusieurs threads.

    Pour reprendre le problème avec les listeners sur un multiprocesseur, voici ce qui pourrait se produire (avec une très faible proba, je te l'accorde):
    - P2 fait un addListener(listener).
    - 5 minutes après, P1 fait un getListeners(), et ne voit pas le listener ajouté par P2, pour P1 getListeners() renvoie toujours un tableau vide.

    Et ça, c'est génant...
    Si c'est génant, c'est qu'il te manque un verrou : ça veut dire qu'au moment où P1 s'attend à trouver un listener, P2 n'a pas encore fini de l'ajouter. Mais pour que P1 sache qu'un listener est censé être là, il lui faut une autre variable (je l'appelle "flag"), sinon il n'a aucun moyen de savoir que P2 a commencé addListener. Et on revient à ce que je dis : le problème est que "flag" et la ListenerList sont dans un état incohérent.

  7. #7
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Ton message et ma modification se sont croisées...

    Bien sûr, c'est dans le cas où P2 fait d'abord print(b) et ensuite print(a), et que ça affiche b = 2 et a = 0 (qui n'est pas un état intermédiaire possible)...

    Et ça ça ne peut se produire que sur une machine multi-processeur (c'est un effet de cache)...

    Si c'est génant, c'est qu'il te manque un verrou : ça veut dire qu'au moment où P1 s'attend à trouver un listener, P2 n'a pas encore fini de l'ajouter.
    Non non, même si P2 a fini de l'ajouter depuis 5 minutes, P1 peut ne pas le voir (si l'invalidation des caches n'a pas eu lieu, car pas de synchronized).
    C'est pour ça que je pense qu'il faut que getListeners() de EventListenerList (tout du moins la première ligne) soit synchronized...

  8. #8
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    Pour moi il n'y a pas de problème. : le mot-clef synchronized n'est pas le seul à permettre un code thread-safe

    Je m'explique : on est d'accord sur le fait que les méthodes add() et remove() doivent être synchronisé pour éviter des "pertes" de données...

    Toutefois, si tu regardes bien le code de ces deux méthodes, tu t'aperçois qu'elles travailles sur un tableau d'objets temporaire, et que l'attribut listenerList n'est modifié qu'à la fin du traitement. Par exemple dans la méthode add() on peut trouver ceci (j'ai enlevé les vérifications et cas particuliers) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    	    // Otherwise copy the array and add the new listener
    	    int i = listenerList.length;
    	    Object[] tmp = new Object[i+2];
    	    System.arraycopy(listenerList, 0, tmp, 0, i);
     
    	    tmp[i] = t;
    	    tmp[i+1] = l;
     
    	    listenerList = tmp;
    Ainsi à chaque opération add() ou remove(), un nouveau tableau est créé, et ensuite sa référence est assigné dans l'attribut listenerList. Or l'assignement est forcément une opération atomique (sauf pour les types long et double où cela peut dépendre de l'implémentation car ils sont codés sur 64bits).



    Et si tu regardes bien la méthodes getListeners(), elle copie la référence de listenerList dans une variable locale :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Object[] lList = listenerList;
    Cette opératio n'a pas besoin d'être synchronisé puisque l'accès à listenerList est une opération atomique.

    Et comme le reste de la méthode travaille sur la variable locale lList. De ce fait si un add() ou remove() est appelé dans un autre thread, cela modifiera l'attribut listenerList mais n'aura aucun impact sur notre méthode getListeners() car elles travaille sur une copie :
    [list][*]Dans le meilleur des cas le add()/remove() modifie la référence de listenerList juste avant l'affection dans lList, et dans ce cas la méthode getListeners() travaillera avec les toutes dernières données.[*]Dans le pire des cas, listenerList sera modifié entre l'affection de lList et la boucle de traitement, et dans ce cas lList correspond à une version "obsolète" de listenerList, et possèdera alors un listener en plus ou en moins... mais cela ne pose pas de problème d'exécution et est acceptable (grosso modo cela reviendrait à ce que la méthode add()/remove() soit exécuté après le getListeners()).




    Le fait de ne modifier la référence de l'attribut qu'à la fin des méthodes add()/remove(), et d'utiliser une copie de la référence dans les autres méthodes est une optimisation permettant d'éviter de tout synchroniser.

    Un exemple un peu plus explicite :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class MaClasse {
     
    	String value;
     
    	public synchronized void setValue(String value) {
    		this.value = value.trim();
     
    		// On remplace les caractères spéciaux HTML :
    		this.value = this.value.replaceAll("&", "&amp;");
    		this.value = this.value.replaceAll("<", "&lt;");
    		this.value = this.value.replaceAll(">", "&gt;");
     
    		// On remplace les fin de ligne par des <BR/>
    		this.value = this.value.replaceAll("\n", "<BR/>");
     
    		// On supprime les espaces multiples :
    		this.value = this.value.replaceAll("\\s+", " ");
    	}
     
    	public synchronized String getValue() {
    		return this.value;
    	}
     
    	public synchronized void method() {
     
    		// Plusieurs traitements sur this.value
     
    	}
     
    }
    Dans ce code, on est obligé de synchroniser les trois méthodes, sinon on pourrait avoir une valeur de this.value lorsqu'on y accède entre deux replaceAll()...

    Le problème c'est que toutes les méthodes de la classe qui utiliseront l'attribut this.value devront être synchronisé... ce qui rend le tout plus complexe et peut multiplier les 'conflits' sur les verrous...


    Or, dans ce cas, il est possible de faire un code thread-safe sans utiliser le mot-clef synchronized.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class MaClasse {
    	String value;
     
    	public void setValue(String value) {
    		String lValue = value.trim();
     
    		// On remplace les caractères spéciaux HTML :
    		lValue = lValue.replaceAll("&", "&amp;");
    		lValue = lValue.replaceAll("<", "&lt;");
    		lValue = lValue.replaceAll(">", "&gt;");
     
    		// On remplace les fin de ligne par des <BR/>
    		lValue = lValue.replaceAll("\n", "<BR/>");
     
    		// On supprime les espaces multiples :
    		lValue = lValue.replaceAll("\\s+", " ");
     
    		// On ne modifie l'attribut qu'à la fin du traitement :
    		this.value = lValue;
    	}
     
    	public String getValue() {
    		return this.value;
    	}
     
    	public void method() {
     
    		String lValue = this.value;
    		// Plusieurs traitement sur lValue
     
    	}
    }
    On respecte seulement deux règles simples :
    • Chaque méthode qui modifie l'attribut travaille sur une variable locale, et n'affecte la référence de l'attribut qu'à la fin de son traitement (une fois que la valeur est correcte).
    • Chaque méthode qui utilise l'attribut n'effecte qu'un accès à sa référence, en effectuant une copie dans une variable locale s'il a besoin d'effectuer plusieurs traitements.


    Et dans le pire des cas on travaille sur une copie 'obsolète' de l'attribut, mais cela peut être acceptable dans bien des cas (et permet d'éviter de tout synchronisé).



    a++

  9. #9
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    En fait, adiGuba, avant c'est ce que je pensais.

    C'était d'ailleurs l'objet de 2 posts (très semblables) que j'avais fait sur le forum (le 1er, le 2e).

    Mais en fait, après en avoir discuté il y a quelques mois avec mon prof de synchronisation, je crois que cette phrase n'est pas vraie:
    Cette opération n'a pas besoin d'être synchronisée puisque l'accès à listenerList est une opération atomique.
    En effet, le synchronized effectue 2 choses : l'exclusion mutuelle (qui assure l'atomicité), mais pas seulement, il permet aussi de mettre en cohérence les caches des processeurs.
    Donc certes avec une opération atomique tu résouds un problème, mais pas le 2e.

    Sur un mono-processeur, ça n'est pas génant, et a priori, sur un dual core (qui partagent le même cache) non plus, mais sur un vrai multi-processeur, la cohérence n'est pas garantie...

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    548
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 548
    Points : 635
    Points
    635
    Par défaut
    Je n'avais pas compris que tu avais en tête des problèmes de cache processeur. Ca me parait quand même louche ce que tu dis, pour moi les caches sont invalidés à chaque écriture, mais bon je ne connais pas le sujet.

    Quoiqu'il en soit si tu prends en compte ce genre de problème, il y a un paquet d'autres endroits où ça causerait les mêmes symptomes.

  11. #11
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Apparemment, un article d'IBM semble confirmer ma pensée:
    http://www-128.ibm.com/developerwork...-threads1.html

    Citation Envoyé par IBM
    What does synchronized really mean?

    Most Java programmers think of a synchronized block or method entirely in terms of enforcing a mutex (mutual exclusion semaphore) or defining a critical section (a block of code which must run atomically). While the semantics of synchronized do include mutual exclusion and atomicity, the reality of what happens prior to monitor entry and after monitor exit is considerably more complicated.

    The semantics of synchronized do guarantee that only one thread has access to the protected section at one time, but they also include rules about the synchronizing thread's interaction with main memory. A good way to think about the Java Memory Model (JMM) is to assume that each thread is running on a separate processor, and while all processors access a common main memory space, each processor has its own cache that may not always be synchronized with main memory. In the absence of synchronization, it is allowable (according to the JMM) for two threads to see different values in the same memory location. When synchronizing on a monitor (lock), the JMM requires that this cache be invalidated immediately after the lock is acquired, and flushed (writing any modified memory locations back to main memory) before it is released. It's not hard to see why synchronization can have a significant effect on program performance; flushing the cache frequently can be expensive.

  12. #12
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    En effet tu as raison : le cache processeur peut poser des problèmes.

    J'ai un peu de mal avec toutes les notions spécifiques au mltiprocesseur, mais je pense que le mot-clef volatile sur listenerList devrait amplement suffire pour régler le problème, et serait moins couteux que de synchroniser getListener()...

    Maintenant, il faut également voir comment fonctionne ce cache processeur, et dans quel mesure il conserve des données sans les mettre à jour avec le heap...


    a++

  13. #13
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par adiGuba
    J'ai un peu de mal avec toutes les notions spécifiques au mltiprocesseur, mais je pense que le mot-clef volatile sur listenerList devrait amplement suffire pour régler le problème, et serait moins couteux que de synchroniser getListener()...
    Ouais, soit synchroniser juste "lList = listenerList", soit mettre listenerList en volatile... les deux fonctionneraient je pense.

    Citation Envoyé par adiGuba
    il faut également voir comment fonctionne ce cache processeur, et dans quel mesure il conserve des données sans les mettre à jour avec le heap...
    À mon avis ça ne peut se produire que dans des cas extrêmement rares. Déjà il faut être en multi-processeurs, c'est rare... et après faire un getListeners() alors que listenerList est encore en cache et qu'entre temps un add ait eu lieu...

    Mais ça n'est pas impossible.
    Ce qui pourrait faire par exemple, on pourrait imaginer, qu'on ajoute un listener à un bouton (sur un processeur P1), et que quand on clique sur le bouton rien ne se passe... (si l'EDT est exécuté sur un processeur P2)

  14. #14
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par ®om
    Ouais, soit synchroniser juste "lList = listenerList", soit mettre listenerList en volatile... les deux fonctionneraient je pense.
    Je viens de tomber sur un bug-report à ce sujet : Increase thread safety of EventListenerList
    Et c'est exactement ce qui est préconisé : volatible, synchronized ou AtomicReference (qui utilise un volatile en interne)


    a++

  15. #15
    Membre expert
    Avatar de ®om
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 815
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 815
    Points : 3 080
    Points
    3 080
    Par défaut
    Citation Envoyé par adiGuba
    Je viens de tomber sur un bug-report à ce sujet : Increase thread safety of EventListenerList
    Et c'est exactement ce qui est préconisé : volatible, synchronized ou AtomicReference (qui utilise un volatile en interne)


    a++
    OK, j'ai rajouté un commentaire, et j'ai voté pour ce bug...
    Si vous pouviez faire de même (voter) pour qu'il soit corrigé...

    EDIT: j'ai rajouté un lien vers ce topic dans mon article sur les listeners, à la fin de la section IV-B-2. EventListenerList.

Discussions similaires

  1. Recupérer une erreur d'un batch dans un vbs
    Par Pitbull7 dans le forum Windows
    Réponses: 1
    Dernier message: 06/10/2005, 21h10
  2. Réponses: 8
    Dernier message: 13/09/2005, 21h05
  3. librairie introuvable! Erreur"Pas d'objet dans ce contr
    Par vins111282 dans le forum Access
    Réponses: 5
    Dernier message: 16/05/2005, 14h07
  4. erreur de syntaxe javascript dans ma page
    Par Oluha dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 01/02/2005, 14h53
  5. [CONNECTION] Erreur lors du connect dans le fichier C
    Par Petey dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 19/04/2004, 18h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo