Comment l'événement répartit-il le fil de discussion?
Avec l'aide de personnes sur stackoverflow j'ai été en mesure d'obtenir les éléments suivants code de travail d'une interface simple compte à rebours (qui affiche simplement une fenêtre de comptage en secondes). Mon principal problème avec ce code, il est invokeLater
choses.
Autant je comprends invokeLater
il envoie une tâche à l'événement expédition fil (HAE), et puis l'EDT exécute cette tâche chaque fois qu'il "peut" (quoi que cela signifie). Est ce que le droit?
À ma connaissance, le code fonctionne comme ceci:
- Dans le
main
méthode que nous utilisonsinvokeLater
pour afficher la fenêtre (showGUI
méthode). En d'autres termes, le code de l'affichage de la fenêtre seront exécutés dans l'EDT. - Dans le
main
méthode, nous avons également démarrer lecounter
et le compteur (par construction) est exécutée dans un autre thread (donc il n'est pas dans le cas de dispatching thread). Droit? - La
counter
est exécutée dans un thread séparé et régulièrement, il appelleupdateGUI
.updateGUI
est censé mettre à jour l'interface graphique. Et le GUI est de travailler dans l'EDT. Donc,updateGUI
devrait également être exécutée dans l'EDT. C'est la raison pour laquelle le code de laupdateGUI
est enfermé dansinvokeLater
. Est ce que le droit?
Ce n'est pas clair pour moi, c'est pourquoi nous l'appelons le counter
de l'EDT. De toute façon, il n'est pas exécuté dans l'EDT. Il commence immédiatement, un nouveau thread et le counter
est exécutée. Donc, pourquoi ne pouvons-nous pas appel à la counter
dans la méthode main après la invokeLater
bloc?
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class CountdownNew {
static JLabel label;
//Method which defines the appearance of the window.
public static void showGUI() {
JFrame frame = new JFrame("Simple Countdown");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
label = new JLabel("Some Text");
frame.add(label);
frame.pack();
frame.setVisible(true);
}
//Define a new thread in which the countdown is counting down.
public static Thread counter = new Thread() {
public void run() {
for (int i=10; i>0; i=i-1) {
updateGUI(i,label);
try {Thread.sleep(1000);} catch(InterruptedException e) {};
}
}
};
//A method which updates GUI (sets a new value of JLabel).
private static void updateGUI(final int i, final JLabel label) {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
label.setText("You have " + i + " seconds.");
}
}
);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
showGUI();
counter.start();
}
});
}
}
source d'informationauteur Roman
Vous devez vous connecter pour publier un commentaire.
Si je comprends votre question correctement, vous êtes me demande pourquoi vous ne pouvez pas faire ceci:
La raison pour laquelle vous ne pouvez pas le faire c'est parce que le planificateur ne garantit pas... juste parce que vous avez appelé
showGUI()
et puis vous avez appelécounter.start()
ne veut pas dire que le code deshowGUI()
sera exécuté avant le code dans la méthode run de l'counter
.Pensez-y de cette façon:
démarre un thread et le threadhoraires un événement asynchrone sur l'EDT qui est chargé de la création de laJLabel
.JLabel
existe de sorte qu'il peut appelerlabel.setText("You have " + i + " seconds.");
Vous avez maintenant une condition de course:
JLabel
doit être créé AVANT lecounter
thread démarre, si elle n'est pas créé avant que le compteur de fil commence, votre compteur de fil sera d'appelsetText
sur un objet non initialisé.Afin de s'assurer que la condition de la course est éliminée nous devons garantir l'ordre d'exécution et une façon de garantie il est d'exécuter
showGUI()
etcounter.start()
de façon séquentielle sur le même fil:Maintenant
showGUI();
etcounter.start();
sont exécutés à partir du même thread, donc laJLabel
sera créé avant lacounter
est commencé.Mise à jour:
Vous sont effectivement de départ la
counter
fil de l'EDT. Si vous avez appelécounter.start()
après lainvokeLater
bloc, le compteur serait probablement commencer à exécuter avant le GUI devient visible. Maintenant, parce que vous êtes la construction de l'interface graphique dans l'EDT, l'interface graphique ne serait pas existent lorsque lecounter
commence à mettre à jour. Heureusement, vous semblez être de transfert de l'interface graphique des mises à jour à l'EDT, ce qui est correct, et depuis le EventQueue est une file d'attente, la première mise à jour qui va arriver après que l'interface graphique a été construit, il devrait donc y avoir aucune raison que cela ne fonctionne pas. Mais ce qui est le point de mise à jour d'une interface graphique qui peut ne pas être visible encore?C'est un hacky solution de contournement autour de la grand nombre de problèmes de concurrence que le Swing de l'API a 😉
Sérieusement, beaucoup de composants Swing sont pas "thread-safe" (certains célèbres programmeurs sont allés jusqu'à appeler Swing "thread hostile"). En ayant un fil unique où toutes les mises à jour sont faites à ce fil hostiles composants que vous êtes en esquivant beaucoup de potentiels problèmes de concurrence. En plus de cela, vous êtes également garanti qu'il doit exécuter le
Runnable
que vous passer à travers à l'aide deinvokeLater
dans un ordre séquentiel.Puis certains coupant les cheveux en quatre:
Et puis:
Vous n'avez pas vraiment démarrer le compteur dans la méthode main. Vous démarrer le compteur dans le run() méthode de l'anonyme
Runnable
qui est exécuté sur l'EDT. Si vous avez vraiment commencer le compteurThread
de l'EDT, pas de la méthode main. Ensuite, parce que c'est un Thread séparé, il n'est pas exécuter sur l'EDT. Mais le compteur certainement est commencé sur l'EDT, pas dans leThread
de l'exécution de lamain(...)
méthode.Il est tatillon, mais toujours important, vu la question je pense.
C'est simple, c'est comme suit
L'étape 1 . Thread Initial également appelé thread principal est créé.
L'étape 2. Créer un exécutable de l'objet et de le passer à invokeLate().
L'étape 3. Cette initialise l'interface graphique, mais ne crée pas de l'interface graphique.
L'étape 4. InvokeLater() horaires de l'objet créé pour l'exécution sur EDT.
L'étape 5. Interface graphique a été créé.
L'étape 6. Tous les événements survenus sera placé dans l'EDT.