Java: mytextarea.setText ("hello") + Thread.sleep (1000) = странный результат - PullRequest
2 голосов
/ 09 января 2010

У меня есть что-то вроде этого:

for(int i=0; i<5; i++){
    mytextarea.setText("hello " + i);
    try{
        Thread.currentThread().sleep(1000); //to give time for users to read
    } catch(Exception e){}
}

Я ожидаю, что в текстовой области отобразится "привет 0", подождите 1 секунду, затем отобразится "привет 1", затем подождите 1 секунду и т. Д.

Но что происходит по-другому, он ждет 5 секунд, а затем отображает «привет 4».

Есть идеи?

Ответы [ 4 ]

12 голосов
/ 09 января 2010

Да - вы в основном блокируете поток пользовательского интерфейса, поэтому его обновление никогда не дойдет до конца.

Спать в потоке пользовательского интерфейса - очень плохая идея.

Если вы хотите сделать что-то подобное, вы должны использовать Timer. (Я предполагаю, что вы используете Swing. Если нет, измените свой вопрос, чтобы указать, какую платформу пользовательского интерфейса вы используете.)

Следует также отметить, что Thread.sleep является статическим методом. Вы используете это, как если бы это был метод экземпляра. По общему признанию Вы называете это "в" текущем потоке, но Ваше использование предполагает, что Вы думаете, что:

Thread t = new Thread(...);
t.start();
t.sleep(1000);

заставит новый поток спать. Это не будет - это заставит текущий поток спать, потому что это то, что Thread.sleep всегда делает. IMO, для Java было ошибкой позволять вам вызывать статические методы таким образом - если вы используете Eclipse, есть возможность сделать это предупреждением или ошибкой.

4 голосов
/ 09 января 2010

Как объяснено в ответе Джона Скита, вы должны использовать таймер, поскольку вы не можете заблокировать EDT и ожидать обновления пользовательского интерфейса. Ниже приведен пример вашего фрагмента кода с использованием таймера Swing.

ActionListener action = new ActionListener() {
    int i = 0;

    public void actionPerfomed(ActionEvent e) {
        mytextarea.setText("hello " + i++);
    }
};
new javax.swing.Timer(1000, action).start();

См. Как использовать Swing Timers в Swing Tutorial для получения дополнительной информации о возможностях таймера.

4 голосов
/ 09 января 2010

Пока ваш код ожидает, никакие события не будут обработаны

http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html

Прочитайте javadoc из javax.swing.SwingUtilities.invokeAndWait () и invokeLater (), это может помочь

РЕДАКТИРОВАТЬ: Благодаря Джону и Сэмюэлю, которые соединили все идеи:

public class Swing extends JPanel {
    JTextField textField;
    static JTextArea textArea;
    static int line = 1;

    public Swing() {
        super(new BorderLayout());
        textArea = new JTextArea(5, 20);
        add(textArea);
    }

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("TextDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new Swing());
        frame.pack();
        frame.setVisible(true);

        ActionListener taskPerformer = new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                textArea.append("Hello " + line++ + "\n");
            }
        };
        if (line < 5) {
            new Timer(1000, taskPerformer).start();
        }
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }
}
2 голосов
/ 09 января 2010

Еще один способ не блокировать поток рассылки событий (EDT) - запустить новый поток:

Thread thread = new Thread(new Runnable() {
    @Override
    public void runt() {
        for (int i=0; i<5; i++) {
            mytextarea.setText("hello " + i);
            try {
                Thread.sleep(1000); //to give time for users to read
            } catch (InterruptedException e) {
                break;    // interrupt the for
            }
        }
    }
});
thread.start();

EDIT:
В общем случае Swing НЕ является потокобезопасным, то есть методы Swing, не помеченные как потокобезопасные, должны вызываться только в EDT. setText() является поточно-ориентированным, поэтому в вышеприведенном коде проблем нет.

Для запуска кода в EDT вы используете invokeAndWait() или invokeLater() из javax.swing.SwingUtilities (или из java.awt.EventQueue).

Подробнее см .: Политика потоков Swing

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...