6 votes

Pourquoi mon interface utilisateur graphique reste-t-elle bloquée même après avoir utilisé SwingUtilities.invokeLater?

J'ai ce ActionListener qui est appelé dans l'EDT. Ma fonction plot() est très lourde en termes de calcul, elle peut facilement prendre cinq secondes. Cela a fait planter l'interface graphique comme prévu. J'ai ajouté le code SwingUtilities.invokeLater et cela plante toujours. L'interface graphique ne devrait-elle pas être réactive maintenant que je lance un thread séparé pour mon calcul lourd?

final ActionListener applyListener = new ActionListener() 
        {
            @CommitingFunction
            public void actionPerformed(ActionEvent arg0) 
            {
                /*Ne faites pas de tracé dans l'EDT :)*/
                SwingUtilities.invokeLater(new Runnable() 
                {
                    public void run() 
                    {
                        plot();
                    }
                });
            }
        };

16voto

Adam Norberg Points 2336

Pas du tout. InvokeLater ne crée pas un nouveau thread. invokeLater existe pour dire explicitement à Swing "utilise le Thread de Dispatching des Événements pour cela, mais pas tout de suite". invoke et invokeLater vous permettent de faire des opérations qui ne sont sûres que pour le Thread de Dispatching des Événements à partir d'autres threads, non pas en les exécutant sur ces threads, mais en demandant à l'EDT de les exécuter.

Votre ActionListener s'exécutera très rapidement, en plaçant le Runnable dans la file d'attente de dispatching d'événements de Swing. Ensuite, une fois qu'il aura atteint ce point, il prendra cinq secondes pour exécuter le plot().

La seule solution de contournement est de réorganiser plot(). Utilisez un SwingWorker (ou une stratégie de multithreading similaire, mais SwingWorker est probablement le meilleur choix pour cela) pour déplacer réellement la logique de plot() sur un thread différent. Ce thread ne peut pas dessiner en toute sécurité car il n'est pas le Thread de Dispatching des Événements de Swing, donc toutes ses opérations de dessin doivent être effectuées via invokeLater(). Pour des raisons d'efficacité, essayez de faire toutes les opérations de dessin en une seule fois, sur un seul invokeLater(), en utilisant les résultats stockés de votre calcul.

3voto

Jason Nichols Points 5055

Vous faites exactement le contraire de ce que vous pensez faire. Au lieu d'exécuter votre thread de calcul en dehors de l'EDT, vous l'appelez explicitement à l'intérieur de celui-ci !

SwingUtilities.invokeLater() met en file d'attente le runnable pour son invocation ultérieure, dans l'EDT ! Vous devriez utiliser SwingWorker à la place.

1voto

Peter Lawrey Points 229686

InvokeLater ajoute une tâche à la file de travail des interfaces graphiques. Elle sera exécutée après que toutes les autres tâches ont été effectuées, cependant elle utilise toujours le thread GUI.

Je vous suggère d'utiliser un ExecutorService.

Comme le suggère @Adam, tout dessin réel doit être fait via invokeLater.

1voto

mavroprovato Points 2278

Vous ne montrez pas ce qui se trouve à l'intérieur de la fonction plot(), mais vous ne devriez pas mettre de peinture là-dedans. Calculez ce que vous voulez dans le nouveau thread, et effectuez la peinture dans l'EDT. Pour cela, il est préférable d'utiliser SwingWorker

1voto

The Vanguardian Points 21

Voici ce que j'ai fait pour l'application de mon entreprise, c'est un peu de code pseudo pour des raisons légales, mais l'idée générale est que si l'écran est non réactif, il redémarrera l'interface GUI. Chaque fois que vous utilisez SwingUtilities pour démarrer l'EDT, dans le même bloc d'initialisation, créez deux threads de surveillance. Un thread se contentera d'effectuer une action sur le thread EDT en utilisant Swing utilities. Un autre thread surveillera le premier thread pour voir s'il est réactif. Le premier thread ne reconnaîtra la réactivité que s'il peut exécuter une commande très simple.

définir isEDTCheck sur true lors de l'exécution normale, false en mode débogage (sinon vous allez être constamment redémarré).

    if (isEDTCheck) {
        new Thread("EDTHeartbeat") {
            @Override
            public void run() {
                Runnable thisThingYouDo = new Runnable() {
                    public void run() {
                        int x = 0;
                    }
                };
                while (true) {
                    // le premier thread dit que nous attendons, alias mauvais état
                    edtwait=true;
                    try {
                        javax.swing.SwingUtilities.invokeAndWait(thisThingYouDo);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    // le premier thread dit que nous n'attendons pas, bon état
                    edtwait=false;
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();

        new Thread("EDTValidator") {
            @Override
            public void run() {
                while (true) {
                    // le premier thread est-il dans un mauvais état ?
                    if (edtwait) {
                        try {
                            Thread.sleep(3000);
                            // après 3 secondes sommes-nous toujours dans un mauvais état ? si c'est le cas, supprimer le cadre initial, afficher une boîte de dialogue en AWT qui ne fait aucune commande
                            if (edtwait) {
                                mainFrame.setVisible(false);
                                new Dialog();  
                            } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

  public class Dialog extends Frame {
    private static final int WIDTH = 400;
    private static final int HEIGHT = 300;
    Frame f = null;
    public Dialog() {
        f = this;
        hasSomethingBeenEntered=false;
        this.setTitle("PROBLÈME DÉTECTÉ DANS L'APPLICATION");
        this.setSize(WIDTH, HEIGHT);
        this.setLocation((int)Toolkit.getDefaultToolkit().getScreenSize().getWidth() - myapp.width, 0);
        Panel p1 = new Panel() {
            @Override
            public void paint(final Graphics g) {
                int left = Dialog.WIDTH/2 - 45; // ne pas utiliser WIDTH masqué par la classe Panel
                int top = Dialog.HEIGHT/2 - 20; // pareil que ci-dessus
                g.drawString("L'APPLICATION A DÉTECTÉ UN PROBLÈME", left, top);
            }
        };
        this.add("Center", p1);

        this.setAlwaysOnTop(true);
                 TextArea tb = new TextArea("L'APPLICATION A DÉTECTÉ UN PROBLÈME MAJEUR\nELLE VA RECOMMENCER DANS 5 SECONDES");
        this.add(tb);
        this.setVisible(true);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        restartApp();

    }

    private void restartApp() {
            Runtime.getRuntime().exec("cmd /c start cmd.exe /K \"cd C:\\Progra~1\\Common~1 && C:\\Progra~1\\Common~1\\MyAppDir\\myjavaapp.jar\"");
            System.exit(0);
       }

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