FlorianGaudon
-
Modifié le 26 janv. 2019 à 06:57
KX
Messages postés16705Date d'inscriptionsamedi 31 mai 2008StatutModérateurDernière intervention 5 juin 2023
-
26 janv. 2019 à 15:04
Bonsoir à tous,
J'ai un petit problème dans le code suivant :
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Ex {
int attribut ;
Timer chrono;
private ActionListener refreshChrono ;
public Ex(){
attribut = 0;
chrono = new Timer(1000,refreshChrono);
refreshChrono = new ActionListener() {
public void actionPerformed(ActionEvent e1)
{
attribut = 1;
}
};
chrono = new Timer(1000,refreshChrono);
chrono.start();
}
public static void main(String[] args){
Ex exmple = new Ex();
System.out.println(exmple.get());
while(true){
System.out.println(exmple.get());
if(exmple.get() == 1) {
System.out.println("CoucouMain");
}
}
}
int get() {
return attribut;
}
}
En effet quand dans le while du main, je print exmple.get() je rentre dans le if alors que si je commente le print je n'y rentre plus..
Si quelqu'un sait m"expliquer pourquoi je suis preneur !
Merci d'avance :)
KX
Messages postés16705Date d'inscriptionsamedi 31 mai 2008StatutModérateurDernière intervention 5 juin 2023126 26 janv. 2019 à 10:38
Bonjour,
C'est une question très intéressante, il m'a fallu quelques minutes avant de trouver la solution, c'est le genre de piège où je pourrais encore me faire avoir.
Déjà on peut simplifier ton code et j'en profite pour te montrer une alternative afin d'éviter d'utiliser du code Swing alors que tu n'as pas d'interface graphique (mais le bug reste le même).
import java.util.concurrent.*;
// import javax.swing.Timer;
public class Ex {
private int attribut;
public Ex() {
// new Timer(1000, e -> attribut = 1).start();
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> attribut = 1, 1L, 1L, TimeUnit.SECONDS);
}
public static void main(String[] args) {
Ex ex = new Ex();
while (true) {
// System.out.println(ex.attribut);
if (ex.attribut == 1) {
System.out.println("CoucouMain");
}
}
}
}
Pour corriger le bug, il faut rajouter le mot clé volatile à ton attribut :
private volatile int attribut;
Tu trouveras beaucoup d'explication sur ce mot clé, par exemple ici, ce qu'il se cache derrière c'est une optimisation de la JVM qui met en cache la valeur de l'attribut, mais il faut la forcer à se mettre à jour.
FlorianGaudon
Messages postés1Date d'inscriptionvendredi 25 janvier 2019StatutMembreDernière intervention26 janvier 2019 26 janv. 2019 à 11:48
Bonjour ! :)
Merci beaucoup pour la réponse précise et rapide ! J'ai simplifié mon code de base volontairement pour que la recherche du bug soit plus simple mais de base j'utilise une interface graphique.
Mais du coup je doit rajouter volatile à tous mes attributs qui peuvent être accéder par la déclaration d'un objet ?
A chaque fois que je vais vouloir faire ex.attribut il faudra que l'attribut concerné soit déclaré en volatile ?!
Merci encore de ta réponse rapide et efficace ! :D
KX
Messages postés16705Date d'inscriptionsamedi 31 mai 2008StatutModérateurDernière intervention 5 juin 2023126
>
FlorianGaudon
Messages postés1Date d'inscriptionvendredi 25 janvier 2019StatutMembreDernière intervention26 janvier 2019 26 janv. 2019 à 15:04
C'est un problème qui ne se pose que parce que tu as un thread d'écriture différent des threads de lecture, si ton objet était construit, modifié et lu par le même thread (ce qui est souvent le cas) il n'y aurait pas de conflit.
volatile
va forcer la JVM à relire la valeur de l'objet entier, donc même s'il n'y a qu'un seul attribut volatile, ils seront tous mis à jour. Mais pour la clarté du code, il vaut mieux ajouter le mot clé volatile à chaque attribut qui serait encore concerné si on supprimait tous les autres attributs.
26 janv. 2019 à 11:48
Merci beaucoup pour la réponse précise et rapide ! J'ai simplifié mon code de base volontairement pour que la recherche du bug soit plus simple mais de base j'utilise une interface graphique.
Mais du coup je doit rajouter volatile à tous mes attributs qui peuvent être accéder par la déclaration d'un objet ?
A chaque fois que je vais vouloir faire ex.attribut il faudra que l'attribut concerné soit déclaré en volatile ?!
Merci encore de ta réponse rapide et efficace ! :D
26 janv. 2019 à 15:04
va forcer la JVM à relire la valeur de l'objet entier, donc même s'il n'y a qu'un seul attribut volatile, ils seront tous mis à jour. Mais pour la clarté du code, il vaut mieux ajouter le mot clé volatile à chaque attribut qui serait encore concerné si on supprimait tous les autres attributs.
Voir l'article de Brian Goetz (architecte du langage Java) : Java theory and practice : Managing volatility