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

EDT/SwingWorker Java Discussion :

Suspendre, reprendre, arrêter un Thread et interagir avec l'interface


Sujet :

EDT/SwingWorker Java

  1. #1
    Membre confirmé
    Homme Profil pro
    Ed Nat
    Inscrit en
    Janvier 2013
    Messages
    325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ed Nat
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2013
    Messages : 325
    Points : 559
    Points
    559
    Par défaut Suspendre, reprendre, arrêter un Thread et interagir avec l'interface
    Bonjour,
    Il s'agit d'un problème faisant suite à 1 discussion précédente, que je délocalise, pour ne pas polluer le sujet initial de l'auteur (voir Actions concurrentes dans l'EDT)

    Le sujet est assez simple, et la solution doit l'être tout autant...
    mais pour l'instant, je ne l'ai pas trouvée.

    Il s'agit d'exécuter une action longue dans Swing, sans bloquer l'EDT, et en donnant la possibilité à l'utilisateur :
    • de démarrer la tâche longue
    • de la mettre en pause ou de la reprendre
    • de la stopper définitivement


    J'ai une classe permettant de gérer ce type de tâche, mal, puisque la méthode sensée mettre la tâche en pause bloque l'EDT (méthode sleep en rouge)
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public abstract class MyTask implements Runnable {
    	private int delai = 1000;
    	private volatile Thread currentThread = null;
    
    	public void run() {
    		currentThread = Thread.currentThread();
    		while (!currentThread.isInterrupted() && continueCondition()) {
    			doMyTask();
    			try {
    				Thread.sleep(delai);
    			} catch (InterruptedException exception) {
    				onCancelled();
    				break;
    			}
    		}
    	}
    
    	public void cancel() {
    		currentThread.interrupt();
    	}
    	//Pose problème : méthode bloquant l'EDT
    	public void sleep() {
    		synchronized (this) {
    			try {
    				while (!currentThread.isInterrupted()) {
    					this.wait();
    				}
    			} catch (InterruptedException e) {
    				onCancelled();
    			}
    		}
    	}
    
    	public void wakeUp() {
    		synchronized (this) {
    			this.notify();
    		}
    	}
    
    	public abstract void doMyTask();
    
    	public abstract boolean continueCondition();
    
    	public abstract void onCancelled();
    
    }
    • doMyTask permet d'implémenter 1 tâche atomique (1 parcours de boucle...)
    • continueCondition est ta condition de continuité (de la boucle par exemple)
    • onCancelled, la méthode à implémenter en cas d'annulation



    Ci dessous une classe de test implémentant MyTask (avec une tâche bidon consistant à boucler...):
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
     
    public class TestThreads extends JFrame implements ActionListener {
    	private static final long serialVersionUID = 1L;
    	private JPanel mainPanel;
    	private JButton btStart;
    	private JButton btStop;
    	private JButton btSleep;
    	private JButton btWakeUp;
    	private MyTask mytask;
    	private Thread currentThread;
    	private JLabel lblCompteur;
    	private int compteur = 0;
     
    	public static void main(String[] args) {
    		javax.swing.SwingUtilities.invokeLater(new Runnable() {
    			private TestThreads t;
     
    			@Override
    			public void run() {
    				t = new TestThreads();
     
    			}
    		});
     
    	}
     
    	public TestThreads() {
    		super();
    		// Tâche
    		mytask = new MyTask() {
     
    			@Override
    			public void onCancelled() {
    				compteur = 0;
    				updateLblCompteur();
    			}
     
    			@Override
    			public void doMyTask() {
    				compteur++;
    				updateLblCompteur();
    			};
     
    			@Override
    			public boolean continueCondition() {
    				return compteur < 10000;
    			}
    		};
     
    		// Fenêtre
    		JFrame fen = new JFrame("Test threads");
    		fen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		fen.setLayout(new BorderLayout());
    		mainPanel = new JPanel(new FlowLayout());
    		lblCompteur = new JLabel("0");
    		mainPanel.add(lblCompteur);
    		btStart = getButton("Start");
    		btStop = getButton("Stop");
    		btSleep = getButton("Sleep");
    		btWakeUp = getButton("Reveil");
    		fen.add(mainPanel, BorderLayout.NORTH);
    		fen.pack();
    		fen.setVisible(true);
    	}
     
    	/**
             * Création d'un bouton
             * 
             * @param text
             * @return
             */
    	private JButton getButton(String text) {
    		JButton bt = new JButton(text);
    		bt.addActionListener(this);
    		mainPanel.add(bt);
    		return bt;
    	}
     
    	private void updateLblCompteur() {
    		SwingUtilities.invokeLater(new Runnable() {
     
    			@Override
    			public void run() {
    				lblCompteur.setText(compteur + "");
    			}
    		});
    	}
     
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		if (e.getSource().equals(btStart)) {
    			currentThread = new Thread(mytask);
    			currentThread.start();
    		}
    		if (e.getSource().equals(btStop))
    			mytask.cancel();
    		if (e.getSource().equals(btSleep)) {
    			// Problème !!!!!!!!! Méthode bloquante
    			mytask.sleep();
    			System.out.println("pas sleep");
    		}
    		if (e.getSource().equals(btWakeUp))
    			mytask.wakeUp();
    	}
    }
    Logiquement, le thread de la tâche n'est pas lancé dans l'EDT :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    currentThread = new Thread(mytask);
    currentThread.start();
    donc pourquoi le wait sur mytask bloque t-il l'EDT ?

  2. #2
    Membre confirmé
    Homme Profil pro
    Ed Nat
    Inscrit en
    Janvier 2013
    Messages
    325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ed Nat
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2013
    Messages : 325
    Points : 559
    Points
    559
    Par défaut
    Bonjour,
    Le problème est résolu en pratique (mais pas en théorie), et je n'ai pas la réponse à tout :
    je faisait de toute évidence une mauvaise utilisation du wait/notify :
    Après lecture d'un article assez bien fait sur l'endormissement des Threads, je pensais :
    • Que le wait sur un object (o.wait() ) mettait en attente le Thread qui avait fait cet appel
    • Qu'il suffisait d'envoyer un notify à l'objet (o.notify() ) pour que le Thread endormi se réveille...


    Ce qui donnait :
    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
     
    //Pose problème : méthode bloquant l'EDT
    	public void sleep() {
    		synchronized (this) {
    			try {
    				this.wait();
    			} catch (InterruptedException e) {
    				onCancelled();
    			}
    		}
    	}
     
    	public void wakeUp() {
    		synchronized (this) {
    			this.notify();
    		}
    	}
    En pratique, j'ai constaté :
    Que le wait endormait tout, y compris l'EDT de Swing (???)
    Que le notify était bien notifié, mais laissait le Thread à l"état endormi (???)

    J'ai bien pensé à la classe SwingWorker, mais pas de pause, ni de reprise implémentée par défaut sur le SwingWorker, et l'implémentation de mon code dans une classe dérivée de SwingWorker aboutit aux mêmes résultats : app figée et reprise impossible du thread.

    Solution trouvée :
    Finalement, j'ai ajouté une variable d'état (au départ un booléen nommé suspended, logiquement protégé des accès concurrents.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    private volatile boolean suspended=false;
    Si suspended est vrai, on met la tâche en wait :
    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
     
    public void run() {
    	currentThread = Thread.currentThread();
    	while (!currentThread.isInterrupted() && continueCondition()) {
    		doMyTask();
    		try {
    			if (suspended)) {
    				synchronized (this) {
    					while (suspended)
    						wait();
    				}
    			}
    			Thread.sleep(delai);
    		} catch (InterruptedException exception) {
    			//indispensable pour sortir de l'attente
    			break;
    		}
    	}
    }
    De quoi suspendre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    public synchronized void sleep() {
    	suspended=true;
    }
    Et réveiller (en notifiant : indispensable !)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    public synchronized void wakeUp() {
    	suspended=false;
    	notify();
    }
    Bon, ça fonctionne, mais je pense avoir réinventé la roue là (il doit bien y avoir des classes java qui sachent par défaut faire ça)
    et je ne sais toujours pas pourquoi, précédemment, le wait bloquait l'EDT...

  3. #3
    Expert confirmé
    Profil pro
    Inscrit en
    Août 2006
    Messages
    3 274
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 3 274
    Points : 4 141
    Points
    4 141
    Par défaut
    Je n'ai pas pris le temps de bien lire ton problème mais connais-tu ce lien, sur les bonnes pratiques :
    http://docs.oracle.com/javase/1.5.0/...precation.html

  4. #4
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Citation Envoyé par kox2ee Voir le message
    Bon, ça fonctionne, mais je pense avoir réinventé la roue là (il doit bien y avoir des classes java qui sachent par défaut faire ça)
    et je ne sais toujours pas pourquoi, précédemment, le wait bloquait l'EDT...
    Merci. Il me reste à tester avec les chants d'oiseaux. Ca risque d'être un peu plus long que pour le diaporama.
    C'est en respectant les autres que l'on se fait respecter.

  5. #5
    Membre confirmé
    Homme Profil pro
    Ed Nat
    Inscrit en
    Janvier 2013
    Messages
    325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ed Nat
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2013
    Messages : 325
    Points : 559
    Points
    559
    Par défaut
    Citation Envoyé par fr1man Voir le message
    Je n'ai pas pris le temps de bien lire ton problème mais connais-tu ce lien, sur les bonnes pratiques :
    http://docs.oracle.com/javase/1.5.0/...precation.html
    Merci, heu oui, mais je l'avais lu un peu en diagonale :-{, sinon j'aurais perdu moins de temps je crois

    Pour info, je met la version complète et testable de la classe MyTask qui fonctionne (sans doute perfectible) et dont le but est de fournir un cadre d'implémentation pour tâche looooongue, à exécuter dans un thread interruptible (pause, reprise, arrêt) et communiquant son avancement (progression ou changement de statut).

    Listener pour communiquer Etat et progression :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public interface TaskListener extends EventListener {
    	public void statusChange(TaskStatus status);
     
    	public void progressChange(int counter);
    }
    La tâche interruptible (qui sera à surdéfinir) :
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
     
    public abstract class MyTask implements Runnable {
    	public enum TaskStatus {
    		NEW("Nouveau"), STARTED("Démarré"), SUSPENDED("Suspendu"), FINISHED("Terminé"), CANCELLED("Annulé");
    		protected String label;
     
    		private TaskStatus(String label) {
    			this.label = label;
    		}
     
    		public String toString() {
    			return label;
    		}
    	}
     
    	private final EventListenerList listeners = new EventListenerList();
    	private int delai = 10;
    	private volatile Thread currentThread = null;
    	private volatile TaskStatus status;
    	private int counter;
     
    	public MyTask() {
    		super();
    		status = TaskStatus.NEW;
    		counter = 0;
    	}
     
    	public void run() {
    		currentThread = Thread.currentThread();
    		setStatus(TaskStatus.STARTED);
    		while (!currentThread.isInterrupted() && continueCondition()) {
    			doMyTask();
    			setCounter(++counter);
    			try {
    				if (status.equals(TaskStatus.SUSPENDED)) {
    					synchronized (this) {
    						while (status.equals(TaskStatus.SUSPENDED))
    							wait();
    					}
    				}
    				Thread.sleep(delai);
    			} catch (InterruptedException exception) {
    				setStatus(TaskStatus.CANCELLED);
    				break;
    			}
    		}
    		setStatus(TaskStatus.FINISHED);
    	}
     
    	public void cancel() {
    		currentThread.interrupt();
    	}
     
    	public synchronized void sleep() {
    		setStatus(TaskStatus.SUSPENDED);
    	}
     
    	public synchronized void wakeUp() {
    		setStatus(TaskStatus.STARTED);
    		notify();
    	}
     
    	protected void fireStatusChanged(TaskStatus oldStatus, TaskStatus newStatus) {
    		if (!oldStatus.equals(newStatus)) {
    			for (TaskListener listener : getTaskListeners())
    				listener.statusChange(newStatus);
    		}
    	}
     
    	protected void fireProgressChanged(int progress) {
    		for (TaskListener listener : getTaskListeners())
    			listener.progressChange(progress);
    	}
     
    	/**
             * @return the status
             */
    	public TaskStatus getStatus() {
    		return status;
    	}
     
    	/**
             * @param status
             *            the status to set
             */
    	public void setStatus(TaskStatus status) {
    		if (!(this.status.equals(TaskStatus.CANCELLED) && !status.equals(TaskStatus.STARTED))) {
    			fireStatusChanged(this.status, status);
    			this.status = status;
    		}
    		if (this.status.equals(TaskStatus.CANCELLED))
    			setCounter(0);
    	}
     
    	/**
             * @param counter
             *            the counter to set
             */
    	public void setCounter(int counter) {
    		this.counter = counter;
    		fireProgressChanged(counter);
    	}
     
    	public void addTaskListener(TaskListener listener) {
    		listeners.add(TaskListener.class, listener);
    	}
     
    	public TaskListener[] getTaskListeners() {
    		return listeners.getListeners(TaskListener.class);
    	}
     
    	/**
             * @param delai
             *            the delai to set
             */
    	public void setDelai(int delai) {
    		this.delai = delai;
    	}
     
    	public abstract void doMyTask();
     
    	public abstract boolean continueCondition();
     
    }
    Un exemple de mise en oeuvre (Swing) :
    Exécution d'une tâche longue (boucle temporisée de 0 à 10 000), donnant son état d'avancement, ses changements d'état (démarré, interrompu, annulé...), et interruptible via l'interface graphique (pause/reprise, arrêt/démarrage)



    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
     
    public class TestThreadsMyTask implements ActionListener {
    	private static final long serialVersionUID = 1L;
    	private JPanel mainPanel;
    	private JButton btStart;
    	private JButton btStop;
    	private JButton btSleep;
    	private JButton btWakeUp;
    	private MyTask mytask;
    	private Thread currentThread;
    	private JLabel lblCompteur;
    	private JLabel lblStatus;
    	private int compteur = 0;
     
    	public static void main(String[] args) {
    		javax.swing.SwingUtilities.invokeLater(new Runnable() {
    			@Override
    			public void run() {
    				new TestThreadsMyTask();
    			}
    		});
    	}
     
    	public TestThreadsMyTask() {
    		// Tâche
    		mytask = new MyTask() {
     
    			@Override
    			public void doMyTask() {
    				compteur++;
    			};
     
    			@Override
    			public boolean continueCondition() {
    				return compteur < 10000;
    			}
    		};
     
    		mytask.addTaskListener(new TaskListener() {
     
    			@Override
    			public void statusChange(final TaskStatus status) {
    				switch (status) {
    				case STARTED:
    					setBtStatus(false, true, true, false);
    					break;
    				case CANCELLED:
    				case FINISHED:
    					setBtStatus(true, false, false, false);
    					compteur = 0;
    					break;
    				case SUSPENDED:
    					setBtStatus(false, true, false, true);
    					break;
    				default:
    					break;
    				}
    				SwingUtilities.invokeLater(new Runnable() {
    					@Override
    					public void run() {
    						lblStatus.setText(status.toString());
    					}
    				});
     
    			}
     
    			@Override
    			public void progressChange(int counter) {
    				updateLblCompteur(counter);
    			}
    		});
    		// Fenêtre
    		JFrame fen = new JFrame("Test threads");
    		fen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		fen.setLayout(new BorderLayout());
    		mainPanel = new JPanel(new FlowLayout());
    		lblCompteur = new JLabel("0");
    		mainPanel.add(lblCompteur);
     
    		lblStatus = new JLabel("");
     
    		mainPanel.add(lblStatus);
     
    		btStart = getButton("Start");
    		btStop = getButton("Stop");
    		btSleep = getButton("Sleep");
    		btWakeUp = getButton("Wake up !");
    		btStart.setEnabled(true);
    		fen.add(mainPanel, BorderLayout.NORTH);
    		fen.setLocationRelativeTo(null);
    		fen.pack();
    		fen.setVisible(true);
    	}
     
    	/**
             * Création d'un bouton
             * 
             * @param text
             * @return
             */
    	private JButton getButton(String text) {
    		JButton bt = new JButton(text);
    		bt.addActionListener(this);
    		bt.setEnabled(false);
    		mainPanel.add(bt);
    		return bt;
    	}
     
    	/**
             * mise à jour du status des boutons
             */
    	private void setBtStatus(boolean sbtStart, boolean sbtStop, boolean sbtSleep, boolean sbtWakeUp) {
    		btStart.setEnabled(sbtStart);
    		btStop.setEnabled(sbtStop);
    		btSleep.setEnabled(sbtSleep);
    		btWakeUp.setEnabled(sbtWakeUp);
    	}
     
    	private void updateLblCompteur(final int counter) {
    		SwingUtilities.invokeLater(new Runnable() {
     
    			@Override
    			public void run() {
    				lblCompteur.setText(counter + "");
    			}
    		});
    	}
     
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		if (e.getSource().equals(btStart)) {
    			currentThread = new Thread(mytask);
    			currentThread.start();
    		}
    		if (e.getSource().equals(btStop))
    			mytask.cancel();
    		if (e.getSource().equals(btSleep)) {
    			mytask.sleep();
    		}
    		if (e.getSource().equals(btWakeUp))
    			mytask.wakeUp();
    	}
    }
    L'instanciation de MyTask impose l'implémentation de :
    • doMyTask : permettant de définir le traitement à effectuer à chaque itération
    • continueCondition : définissant la condition de continuité de la tâche


    mytask.addTaskListener(new TaskListener()... permet d'agir sur le changement de statut ou la progression en implémentant les méthodes :
    • statusChange : pour le changement du statut
    • progressChange : pour la progression


    Finalement, j'ai pas réinventé la roue, j'en ai juste fait une implémentation...
    Résolu pour moi, ce qui n'empêche pas les suggestions
    Images attachées Images attachées  

  6. #6
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Bonsoir

    Je me lance dans l'étude de ta classe. J'ai une première question. dans cette partie de code (mais aussi pour les run) j'ai une erreur
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    			mytask.addTaskListener(new TaskListener() 
    			{
     
    				@Override
    				public void statusChange(final TaskStatus status) 
    				{
    					switch (status) 
    					{
    						case STARTED:
    							setBtStatus(false, true, true, false);
    							break;
    						case CANCELLED:
    						case FINISHED:
    							setBtStatus(true, false, false, false);
    							compteur = 0;
    							break;
    						case SUSPENDED:
    							setBtStatus(false, true, false, true);
    							break;
    						default:
    							break;
    					}
    					SwingUtilities.invokeLater(new Runnable() 
    					{
    						public void run() 
    						{
    							lblStatus.setText(status.toString());
    						}
    					});
    				}
     
    				public void progressChange(int counter) {
    					updateLblCompteur(counter);
    				}
     
    				public void statusChange1(TaskStatus status)
    				{
    					// TODO Auto-generated method stub
     
    				}
    			});
    J'ai une erreur sur le @override
    The method statusChange(MyTask.TaskStatus) of type new TaskListener(){} must override a superclass method

    Et eclipse me propose de supprimer override.
    C'est en respectant les autres que l'on se fait respecter.

  7. #7
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Bon ça marche une fois que j'ai retiré les @override.

    Par contre en mode sleep, on s'attendrait à voir un bouton de réveil ....

    En fait il y est, c'est ma fenêtre qui n'est pas assez grande.
    C'est en respectant les autres que l'on se fait respecter.

  8. #8
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Il y un truc que je ne comprends pas (ou plutôt je pense que tu as fait plusieurs essai et que tu as laissé traîné cela parce que ne créait pas d'erreur. TestThreadsMyTask étend JFrame et dans le constructeur tu écris cela
    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
    33
    34
    35
    		public TestThreadsMyTask() 
    		{
    			super();
    			// Tâche
    			mytask = new MyTask() 
    			{
    				.../...
    			};
     
    			mytask.addTaskListener(new TaskListener() 
    			{
    				.../...
    			});
    			// Fenêtre
    			JFrame fen = new JFrame("Test threads");
    			fen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    			fen.setLayout(new BorderLayout());
    			mainPanel = new JPanel(new FlowLayout());
    			lblCompteur = new JLabel("0");
    			mainPanel.add(lblCompteur);
     
    			lblStatus = new JLabel("");
     
    			mainPanel.add(lblStatus);
     
    			btStart = getButton("Start");
    			btStop = getButton("Stop");
    			btSleep = getButton("Sleep");
    			btWakeUp = getButton("Wake up !");
    			btStart.setEnabled(true);
    			fen.add(mainPanel, BorderLayout.NORTH);
    			fen.setLocationRelativeTo(null);
    			fen.pack();
    			fen.setVisible(true);
    		}
    Donc si je comprends bien tu appelles le constructeur des JFrame (super()) puis tu crées une fenêtre après les définitions de MyTask et c'est cette dernière fenêtre qui est visible. Soit tu ne mets pas extends JFrame, soit tu remplaces fen par this. J'ai vérifié ça marche sans l'extends.
    C'est en respectant les autres que l'on se fait respecter.

  9. #9
    Membre confirmé
    Homme Profil pro
    Ed Nat
    Inscrit en
    Janvier 2013
    Messages
    325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ed Nat
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2013
    Messages : 325
    Points : 559
    Points
    559
    Par défaut
    Citation Envoyé par Patrice Henrio Voir le message
    J'ai une erreur sur le @override
    The method statusChange(MyTask.TaskStatus) of type new TaskListener(){} must override a superclass method

    Et eclipse me propose de supprimer override.
    Bonsoir,
    si tu as créé l'interface :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public interface TaskListener extends EventListener {
    	public void statusChange(TaskStatus status);
     
    	public void progressChange(int counter);
    }
    tu ne dois pas avoir ce problème,
    la création d'un TaskListener oblige bien à surdéfinir (@Override) statusChange et processChange

  10. #10
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Citation Envoyé par kox2ee Voir le message
    Bonsoir,
    si tu as créé l'interface :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    public interface TaskListener extends EventListener {
    	public void statusChange(TaskStatus status);
     
    	public void progressChange(int counter);
    }
    tu ne dois pas avoir ce problème,
    la création d'un TaskListener oblige bien à surdéfinir (@Override) statusChange et processChange
    Pourtant je l'ai bien créé. Mais il me vient un doute, je n'avais peut-être pas sauvegardé et dans ce cas eclipse trouve des erreurs qui n'existent pas. Je vais ré-essayer avec la sauvegarde des fichiers java.
    C'est en respectant les autres que l'on se fait respecter.

  11. #11
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Non c'est toujours pas ça.
    J'ai rajouté @override et il n'en veut toujours pas, alors que je suis bien d'accord qu'il en faut un !
    D'ailleurs l'erreur est :
    The method statusChange(MyTask.TaskStatus) of type new TaskListener(){} must override a superclass method

    et il me dit de supprimer le @override ???
    Je vais voir si d'autres ont eu ce problème.
    C'est en respectant les autres que l'on se fait respecter.

  12. #12
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Bon c'est un bug connnu de eclipse
    https://bugs.eclipse.org/bugs/show_bug.cgi?id=163194

    et discussion sur le sujet
    http://www.developpez.net/forums/d34...acte-override/
    En plus je compile en 1.5
    C'est en respectant les autres que l'on se fait respecter.

  13. #13
    Membre confirmé
    Homme Profil pro
    Ed Nat
    Inscrit en
    Janvier 2013
    Messages
    325
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Ed Nat
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Janvier 2013
    Messages : 325
    Points : 559
    Points
    559
    Par défaut
    Citation Envoyé par Patrice Henrio Voir le message
    Bon c'est un bug connnu de eclipse
    https://bugs.eclipse.org/bugs/show_bug.cgi?id=163194

    et discussion sur le sujet
    http://www.developpez.net/forums/d34...acte-override/
    En plus je compile en 1.5
    C'est bien ta java compliance 1.5 qui provoque l'erreur sur le @Override
    Annotation existante depuis 1.6
    C'est une drôle d'idée de rester en 1.5...

  14. #14
    Membre averti

    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    464
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 464
    Points : 332
    Points
    332
    Par défaut
    Citation Envoyé par kox2ee Voir le message
    non, non, elle n'y est pas (si on parle bien de ce code : classe TestThreadMyTask)

    Si elle y était (par erreur), ce serait juste une méthode qui ne fait rien et qui n'est jamais appelée
    J'ai compris : c'est encore un coup d'eclipse qui a du me demander deux fois d'ajouter les méthodes nécessaires.
    C'est en respectant les autres que l'on se fait respecter.

  15. #15
    Membre à l'essai
    Inscrit en
    Septembre 2009
    Messages
    28
    Détails du profil
    Informations forums :
    Inscription : Septembre 2009
    Messages : 28
    Points : 13
    Points
    13
    Par défaut
    Citation Envoyé par kox2ee Voir le message
    Bonjour,
    Il s'agit d'un problème faisant suite à 1 discussion précédente, que je délocalise, pour ne pas polluer le sujet initial de l'auteur (voir Actions concurrentes dans l'EDT)

    Le sujet est assez simple, et la solution doit l'être tout autant...
    mais pour l'instant, je ne l'ai pas trouvée.

    Il s'agit d'exécuter une action longue dans Swing, sans bloquer l'EDT, et en donnant la possibilité à l'utilisateur :
    • de démarrer la tâche longue
    • de la mettre en pause ou de la reprendre
    • de la stopper définitivement


    J'ai une classe permettant de gérer ce type de tâche, mal, puisque la méthode sensée mettre la tâche en pause bloque l'EDT (méthode sleep en rouge)
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    public abstract class MyTask implements Runnable {
    	private int delai = 1000;
    	private volatile Thread currentThread = null;
    
    	public void run() {
    		currentThread = Thread.currentThread();
    		while (!currentThread.isInterrupted() && continueCondition()) {
    			doMyTask();
    			try {
    				Thread.sleep(delai);
    			} catch (InterruptedException exception) {
    				onCancelled();
    				break;
    			}
    		}
    	}
    
    	public void cancel() {
    		currentThread.interrupt();
    	}
    	//Pose problème : méthode bloquant l'EDT
    	public void sleep() {
    		synchronized (this) {
    			try {
    				while (!currentThread.isInterrupted()) {
    					this.wait();
    				}
    			} catch (InterruptedException e) {
    				onCancelled();
    			}
    		}
    	}
    
    	public void wakeUp() {
    		synchronized (this) {
    			this.notify();
    		}
    	}
    
    	public abstract void doMyTask();
    
    	public abstract boolean continueCondition();
    
    	public abstract void onCancelled();
    
    }
    • doMyTask permet d'implémenter 1 tâche atomique (1 parcours de boucle...)
    • continueCondition est ta condition de continuité (de la boucle par exemple)
    • onCancelled, la méthode à implémenter en cas d'annulation



    Ci dessous une classe de test implémentant MyTask (avec une tâche bidon consistant à boucler...):
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
     
    public class TestThreads extends JFrame implements ActionListener {
    	private static final long serialVersionUID = 1L;
    	private JPanel mainPanel;
    	private JButton btStart;
    	private JButton btStop;
    	private JButton btSleep;
    	private JButton btWakeUp;
    	private MyTask mytask;
    	private Thread currentThread;
    	private JLabel lblCompteur;
    	private int compteur = 0;
     
    	public static void main(String[] args) {
    		javax.swing.SwingUtilities.invokeLater(new Runnable() {
    			private TestThreads t;
     
    			@Override
    			public void run() {
    				t = new TestThreads();
     
    			}
    		});
     
    	}
     
    	public TestThreads() {
    		super();
    		// Tâche
    		mytask = new MyTask() {
     
    			@Override
    			public void onCancelled() {
    				compteur = 0;
    				updateLblCompteur();
    			}
     
    			@Override
    			public void doMyTask() {
    				compteur++;
    				updateLblCompteur();
    			};
     
    			@Override
    			public boolean continueCondition() {
    				return compteur < 10000;
    			}
    		};
     
    		// Fenêtre
    		JFrame fen = new JFrame("Test threads");
    		fen.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		fen.setLayout(new BorderLayout());
    		mainPanel = new JPanel(new FlowLayout());
    		lblCompteur = new JLabel("0");
    		mainPanel.add(lblCompteur);
    		btStart = getButton("Start");
    		btStop = getButton("Stop");
    		btSleep = getButton("Sleep");
    		btWakeUp = getButton("Reveil");
    		fen.add(mainPanel, BorderLayout.NORTH);
    		fen.pack();
    		fen.setVisible(true);
    	}
     
    	/**
             * Création d'un bouton
             * 
             * @param text
             * @return
             */
    	private JButton getButton(String text) {
    		JButton bt = new JButton(text);
    		bt.addActionListener(this);
    		mainPanel.add(bt);
    		return bt;
    	}
     
    	private void updateLblCompteur() {
    		SwingUtilities.invokeLater(new Runnable() {
     
    			@Override
    			public void run() {
    				lblCompteur.setText(compteur + "");
    			}
    		});
    	}
     
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		if (e.getSource().equals(btStart)) {
    			currentThread = new Thread(mytask);
    			currentThread.start();
    		}
    		if (e.getSource().equals(btStop))
    			mytask.cancel();
    		if (e.getSource().equals(btSleep)) {
    			// Problème !!!!!!!!! Méthode bloquante
    			mytask.sleep();
    			System.out.println("pas sleep");
    		}
    		if (e.getSource().equals(btWakeUp))
    			mytask.wakeUp();
    	}
    }
    Logiquement, le thread de la tâche n'est pas lancé dans l'EDT :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    currentThread = new Thread(mytask);
    currentThread.start();
    donc pourquoi le wait sur mytask bloque t-il l'EDT ?
    J'ai testé le code et ai ajouté l'instruction SwingUtilities.isEventDispatchThread() qui permet de savoir si on est dans l'EDT ou pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	//Pose problème : méthode bloquant l'EDT
    	public void sleep() {
                    System.out.println("SwingUtilities: " + SwingUtilities.isEventDispatchThread());
    		synchronized (this) {
    			try {
    				while (!currentThread.isInterrupted()) {
    					this.wait();
    				}
    			} catch (InterruptedException e) {
    				onCancelled();
    			}
    		}
    	}
    Quand on fait l'appel à wait() et bien...on est dans l'EDT !!
    Voilà pourquoi l'EDT est bloqué.
    Maintenant ce que je ne comprends pas c'est pourquoi on est dans l'EDT ? Car tu lances bien un nouveau thread par l'instruction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    			currentThread = new Thread(mytask);
    			currentThread.start();

  16. #16
    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,

    Citation Envoyé par zorglubpok Voir le message
    Maintenant ce que je ne comprends pas c'est pourquoi on est dans l'EDT ? Car tu lances bien un nouveau thread par l'instruction:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    			currentThread = new Thread(mytask);
    			currentThread.start();
    Le nouveau thread exécute le code de la méthode run()... et c'est tout !
    Or ici la méthode sleep() est appelé dans actionPerformed()... qui elle même est exécuté par l'EDT... donc on est bien dans l'EDT.



    En fait la méthode sleep() est implémenté à l'envers : au lieu de mettre en pause le thread représenté par la classe MyTask, elle met en pause le thread appelant (et donc l'EDT dans ce cas).


    Il faut prendre le problème dans l'autre sens : sleep() doit indiquer au thread qu'il doit se mettre en pause, et ce dernier devra prendre cela en compte dès que possible.
    Exemple :
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    abstract class MyTask implements Runnable {
    	private int delai = 1000;
    	private volatile Thread currentThread = null;
    	// Booléen permettant de savoir si le thread est en pause ou pas.
    	// Le mot clef-volatile est obligatoire car sa valeur est lu/modifié
    	// par des threads différents
    	private volatile boolean paused = false;
     
    	public final void run() {
    		currentThread = Thread.currentThread();
    		while (!currentThread.isInterrupted() && continueCondition()) {
    			doMyTask();
    			try {
    				Thread.sleep(delai);
    				// On vérifie si on doit faire une pause ou pas
    				checkPause();
    			} catch (InterruptedException exception) {
    				onCancelled();
    				break;
    			}
    		}
    	}
     
    	public void cancel() {
    		currentThread.interrupt();
    	}
     
     
    	// Ici on se contente de modifier la valeur de l'attribut 'paused'
    	// sans rien faire de plus.
    	// La pause ne sera pas instantanné (c'est trop complexe à faire),
    	// mais lors du prochain traitement (ce qui semblera instantanné pour
    	// l'être humain, à moins que les traitements ne soient super-long).
    	public void sleep() {
    		synchronized (this) {
    			this.paused = true;
    		}
    	}
     
    	// On commence par modifier la valeur de 'paused', puis on fait un notify()
    	// pour le réveiller s'il s'était endormi.
    	public void wakeUp() {
    		synchronized (this) {
    			this.paused = false;
    			this.notify();
    		}
    	}
     
    	// Cette méthode est appellé à intervalle régulier dans le run()
    	// Elle met en pause le thread courant si besoin
    	private void checkPause() throws InterruptedException {
    		synchronized (this) {
    			if (this.paused) {
    				this.wait();
    			}
    		}
    	}
     
    	public abstract void doMyTask();
     
    	public abstract boolean continueCondition();
     
    	public abstract void onCancelled();
     
    }



    a++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Interagir avec le calendrier outlook
    Par JeromeR dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 19/12/2005, 10h23
  2. [Threads] Actions continues avec des threads
    Par MiJack dans le forum Concurrence et multi-thread
    Réponses: 6
    Dernier message: 10/10/2005, 17h32
  3. interagir avec la webcam
    Par black_code dans le forum Modules
    Réponses: 10
    Dernier message: 16/08/2005, 00h52
  4. Réponses: 18
    Dernier message: 06/04/2005, 14h09
  5. Arrêter une Thread brutalement!
    Par Rodrigue dans le forum C++Builder
    Réponses: 2
    Dernier message: 18/01/2004, 21h29

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