Потоки GUI в Java (и SwingUtilities) - PullRequest
2 голосов
/ 01 марта 2012

Я делаю простую игру на Java с использованием Swing и у меня возникают проблемы с зависанием моего GUI спорадически (из-за проблем с многопоточностью) после нажатия кнопки, которая должна вызвать переключение в JPanels.1002 * Я разместил связанную ветку здесь , в которой более подробно рассказывается о текущем коде, который я сейчас использую (хотя я обновил обратный отсчет и все заработало).Судя по ответам на эту тему, мне кажется, что SwingUtilities.invokeLater() или invokeAndWait() может быть тем, что мне нужно для решения проблемы, но я не уверен, где в моем коде это необходимо или как именно это реализовать.

Я не так много знаю о многопоточности и мог бы использовать любую помощь (желательно несколько подробную и с некоторым примером кода), которую я могу получить.Дайте мне знать, если какие-либо подробности будут полезны.

Ответы [ 4 ]

3 голосов
/ 01 марта 2012

См .: Учебник. Параллелизм в Swing

Вообще говоря, поток отправки событий - это один поток, проходящий через очередь событий и обрабатывающий по одному за раз.

SwingUtilities.invokeLater(..) 

помещает Runnable в эту очередь.Таким образом, он будет обработан EDT, когда EDT завершит все в очереди перед ним (вот почему спящий в очереди блокирует другие события, такие как перерисовка).Относительно необычно вызывать invokeLater (..) из самого EDT, хотя бывают ситуации, когда это полезно (обычно в качестве хака).Я не думаю, что я имел законное использование для SwingUtilities.invokeAndWait (..) в течение последних 6 лет.Возможно один раз.

javax.swing.Timer может быть настроен на срабатывание один раз или периодически.Когда он срабатывает, он помещает событие в очередь EDT.Если у вас есть вычислительно-интенсивная обработка, которую необходимо выполнить, рассмотрите возможность использования javax.swing.SwingWorker для выполнения вычислений в другом потоке и верните результат в поточно-ориентированном виде (это также сравнительно редко).

0 голосов
/ 23 декабря 2014

У меня есть класс WorkerThread, который заботится о потоках и текущем / основном потоке графического интерфейса.я поместил свое приложение с графическим интерфейсом в метод construct () WorkerThread, когда событие запускается, чтобы запустить XXXServer, тогда все потоки активируются, и графический интерфейс работает плавно, без остановки.посмотри./ ** * событие Action * * @see java.awt.event.ActionListener # actionPerformed (java.awt.event.ActionEvent) * / public void actionPerformed (ActionEvent ae) {log.info ("actionPerformed begin ..." +ae.getActionCommand ());

    try {
        if (ae.getActionCommand().equals(btnStart.getText())) {
             final int portNumber = 9990;
             try {

                 WorkerThread workerThread = new WorkerThread(){
                    public Object construct(){

                        log.info("Initializing the XXXServer ...");
                        // initializing the Socket Server
                         try {
                            XXXServer xxxServer = new XXXServer(portNumber);
                            xxxServer.start();
                            btnStart.setEnabled(false);                             
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
                            e.printStackTrace();
                        }
                        return null;
                    }
                };workerThread.start();
                } catch (Exception e) {
                    log.info("actionPerformed() Start button ERROR..." + e.getMessage());
                    e.printStackTrace();
             }


        } else if (ae.getActionCommand().equals(btnStop.getText())) {
            log.info("Exit..." + btnStop.getText());
            closeWindow();
        }

    } catch (Exception e) {
        log
            .info("Error in ServerGUI actionPerformed==="
                + e.getMessage());
    }

}
0 голосов
/ 01 марта 2012

Если в вашем коде GUI определена какая-то работа, подобная этой

Runnable doWorkRunnable = new Runnable() {
    @Override
    public void run() { 
        doWork(); 
    }
};

И вы вызываете его, присоединяя его к новому Thread

Thread t = new Thread(doWorkRunnable);
t.start();

Вы выполняете свою работу в потоке графического интерфейса, что приведет к проблемам в приложении Swing.

Вместо этого попробуйте это (позвольте мне упомянуть, что это просто пример использования)

SwingUtilities.invokeLater(doWorkRunnable);

Это помещает вашего Runnable работника в очередь событий AWT и будет выполнять его после завершения предыдущих событий.

РЕДАКТИРОВАНИЕ : Вот полный пример, который выполняет обратный отсчет от 3 до 0, а затем делает все, что вы хотите сделать после обратного отсчета.

public class TestFrame extends JFrame {

    private JPanel contentPane;
    private final Timer timer;
    private TimerTask[] tasks;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    TestFrame frame = new TestFrame();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public TestFrame() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 450, 300);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        final JLabel lblCountdown = new JLabel();
        contentPane.add(lblCountdown, BorderLayout.NORTH);
        JButton btnStart = new JButton("Start");
        contentPane.add(btnStart, BorderLayout.SOUTH);

        timer = new Timer();
        tasks = new TimerTask[4];

        setContentPane(contentPane);

        for (int i = 0; i < 4; i++) {
            final int count = i;
            tasks[i] = new TimerTask() {
                public void run() {
                    EventQueue.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            lblCountdown.setText(count + "");
                        }
                    });
                }
            };
        }

        btnStart.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                for (int i = 0; i < 4; i++) {
                    timer.schedule(tasks[4 - i - 1], (1000 * i), (1000 * (i + 1)));
                }
                // add another timer.schedule(TimerTask)
                // to execute that "move to game screen" task
                TimerTask taskGotoGame = new TimerTask() {
                    public void run() {
                        timer.cancel();
                        JOptionPane.showMessageDialog(null, "Go to game", "Will now", JOptionPane.INFORMATION_MESSAGE);
                        System.exit(0);
                    }
                };
                // and schedule it to happen after ROUGHLY 3 seconds
                timer.schedule(taskGotoGame, 3000);
            }
        });

    }

}
0 голосов
/ 01 марта 2012

Хорошая точка зрения - это документы .В вашем случае это объясняет, как работает SwingUtilities.invokeLater() и где его использовать:

Заставляет doRun.run () выполняться асинхронно в потоке диспетчеризации событий AWT. Этот метод следует использовать, когда поток приложения должен обновить GUI .

Итак, в ваших действиях, которые изменяют GUI, вы должны использовать метод invokeLater, чтобыGUI не замерзнет.

Еще один хороший ресурс - учебники по Java.Они охватывают параллелизм в Swing .

...