SwingWorker в Java 6 1


Часто пользователь может встретить ситуацию, когда программа,  обладающая толстым интерфейсом, реализованным на SWING во время выполнения каких-либо операций, перестает реагировать на действия пользователя. Все просто. Программист реализовал логику программы в потоке, в котором производится отрисовка и отслеживание событий пользовательского интерфейса. Соответственно пользователь, допустим, нажимает на кнопку, вызывается «мощная» процедура, реализующая некоторую логику, программа виснет до момента возвращения управления из процедуры обратно в GUI.  Как быть?

Очевидное решение – создать новый поток выполнения для процедуры, которая будет реализовывать логику. При этом остаются некоторые существенные проблемы. Это в первую очередь отслеживание в потоке GUI результата выполнения процедуры. Вторым моментом является необходимость отслеживания прогресса выполнения процедуры и возможно получения промежуточных результатов выполнения.  Ну, все не так сложно и вполне реализуемо, тем более поддержка многопоточности в JAVA неплохо развита. Только с 6 версии JAVA это будет очевидным изобретением велосипеда. Да, да я в курсе, что реализации SwingWorker есть и для более ранних версий, и обязательно рассмотрю различия в будущих статьях.

Итак, чтобы было интереснее разбирать возможности SwingWorker, реализованного в Java 6, набросаем небольшую программу. Пусть программа выводит на экран диалоговое окно  с одной кнопкой:

Кнопка производит запуск некоторой длительной процедуры, например такой:

    public int doSomeWork()
    {
        try {
                Thread.sleep(SLEEP_TIME);
        } catch (InterruptedException ex) {}
        return 1;
    }

Зададим SLEEP_TIME секунд  10. Нажмем на кнопку и допустим немного увеличим размер окна. В результате получим  в течении 10 секунд  после нажатия на кнопку что-нибудь подобное:

Мало того, мы даже не сможем закрыть это окно в течение указанного периода. То есть программа не может реагировать на события  в течении 10 секунд, т.к. в том же потоке, где обрабатываются события, мы запустили некую процедуру, которая пока не закончит свою работу не даст программе ни на что реагировать.

Давайте попробуем пристроить SwingWorker в нашу программу таким образом, чтобы процедура не блокировала реакцию программы на события.

Взглянем на документацию API Java 6, в той части, что описывает класс SwingWorker. В самом начале так и написано, что данный класс «Абстрактный класс для выполнения длительных взаимодействующих с GUI задач в отдельном потоке». Как раз то, что нам и надо. Подкласс абстрактного класса SwingWorker должен реализовать метод doInBackground () для выполнения вычислений в фоновом потоке. Ну и реализуем:

public class BigBlackBox1 extends SwingWorker<Integer, Object> {
    private static final long SLEEP_TIME=10000;
    public int doSomeWork()
    {
        try {
                Thread.sleep(SLEEP_TIME);
        } catch (InterruptedException ex) {}
        return 1;
    }

    @Override
    protected Integer doInBackground() {
        return new Integer(doSomeWork());
    }
}

Вызов  соответственно будет похож на что-то вроде:

    private void actionForOurButton(java.awt.event.ActionEvent evt) {                                   
        BigBlackBox1 bbb1=new BigBlackBox1();
        bbb1.execute();
    }          

Теперь работа doSomeWork не блокирует обработку событий приложением.

Скажу пару слов как о параметрах класса и как отследить момент и результат выполнения класса.

Параметров у класса два. Первый определяет тип возвращаемых значений по окончании выполнения нашего потока, а второй тип значений промежуточных результатов.

Вы можете определить метод done() в реализованном вами подклассе. Он будет вызван, когда doInBackground () завершит свою работу. Ничего страшного, если вы реализуете анонимный класс на базе своего подкласса, реализуете в нем метод done() и будете  из него производить вызовы методов GUI компонентов. Необходимо сразу отметить, что вызовы методов GUI компонентов приведут к созданию деятельности в потоке GUI, а не в том, в котором происходит работа done().  Ну или передавайте хэндлеры на GUI объекты в конструктор вашего подкласса и организуйте над ними работу в done(). В SwingWorker есть так же метод get() который позволяет получить результат выполнения потока SwingWorker. get() имеет две реализации. В первой случае мы вызываем метод get() и ждем, когда закончится выполнение doInBackground (), фактически блокируя GUI поток, и есть метод  get (long timeout, TimeUnit unit) который будет ждать лишь указанное время, а потом вернет управление через InterruptedException. Лучше всего вызывать метод  get() или в методе done(), который сам по себе вызывается после окончания работы doInBackground(). Или вызвать пару методов isCanceled() и isDone(). Первый из указанных методов возвращает true, если поток  SwingWorker был прерван  преждевременно, а isDone() возвращает true в том случае, если SwingWorker завершился нормально, отработав doInBackground ().

Аспекты получения и отображения промежуточных результатов рассмотрим в других статьях.

Остались вопросы? Пишите.

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


Оставьте комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Мысль на тему “SwingWorker в Java 6

  • fotofaust

    отличная статья, понятная
    хотелось бы больше узнать про «аспекты получения и отображения промежуточных результатов» в JProgressBar