Предотвращение блокировки Swing GUI во время фоновой задачи

#java #swing

#java #многопоточность #качели #swingworker #событие-отправка-поток

Вопрос:

У меня есть приложение swing, в котором хранится список объектов. Когда пользователь нажимает кнопку,

Я хочу выполнить две операции над каждым объектом в списке, а затем, как только это будет завершено, отобразить результаты в JPanel. Я пробовал SwingWorker, Callable amp; Runnable для выполнения обработки, но независимо от того, что я делаю, при обработке списка (что может занять до нескольких минут, поскольку он связан с вводом-выводом) графический интерфейс заблокирован.

У меня такое чувство, что, вероятно, я вызываю потоки или что-то в этом роде, или это может быть связано с функцией построения графиков? Это не многопоточно, так как это очень быстро.

Мне также нужно выполнить два этапа обработки по порядку, так каков наилучший способ убедиться, что второй ожидает первого? Я использовал join(), а затем

 while(x.isAlive())  
{  
        Thread.sleep(1000);  
}
 

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

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

Комментарии:

1. Это может помочь, если вы включите некоторый код, показывающий, как вы используете SwingWorker.

2. Согласен; покажите нам проблемный код.

Ответ №1:

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

Что вам нужно будет сделать, это поместить длительную задачу в другой поток.

Некоторые распространенные способы сделать это — использовать таймеры или SwingWorker .

Учебные пособия по Java содержат много информации об этих вещах в своем уроке по параллелизму.

Чтобы убедиться, что первая задача завершится раньше второй, просто поместите их оба в один поток. Таким образом, вам не придется беспокоиться о том, чтобы правильно синхронизировать два разных потока.

Вот пример реализации SwingWorkerДля вашего случая:

 public class YourTaskSwingWorkerSwingWorker extends SwingWorker<List<Object>, Void> {
    private List<Object> list
    public YourClassSwingWorker(List<Object> theOriginalList){
        list = theOriginalList;
    }

    @Override
    public List<Object> doInBackground() {
        // Do the first opperation on the list
        // Do the second opperation on the list

        return list;
    }

    @Override
    public void done() {
        // Update the GUI with the updated list.
    }
}
 

Чтобы использовать этот код, при запуске события для изменения списка создайте новое SwingWorker и сообщите ему о запуске.

Комментарии:

1. Я почти уверен, что в вопросе уже упоминались потоки и SwingWorker. Кроме того, SwingUtilities.invokeLater() запускает вещи в потоке GUI.

2. Да, я дал своего рода общий ответ на swing threading… вроде как отстой

3. Вчера мне было лень искать Javadoc, но теперь я вижу, что параметр второго типа используется только как параметр для publish() и process() . Я отказываюсь от своего первого комментария.

4. SwingUtilities.invokeLater() выполняет задачи в потоке отправки событий, что вызовет ту же проблему в неопределенное время в будущем. Плохой совет.

5. Именно то, что я искал! Я доволен своими навыками поиска в Google и SE сегодня!

Ответ №2:

Вы неправильно возвращаете поток swing. Я понимаю, что вы используете callable / runnable, но я предполагаю, что вы делаете это неправильно (хотя вы не опубликовали достаточно кода, чтобы знать наверняка).

Базовая структура будет:

 swingMethod() { // Okay, this is a button callback, we now own the swing thread
    Thread t=new Thread(new ActuallyDoStuff());
    t.start();
}

public class ActuallyDoStuff() implements Runnable {
    public void run() {
        // this is where you actually do the work
    }
}
 

Это просто не укладывается у меня в голове, но я предполагаю, что вы либо не выполняете thread.start, а вместо этого вызываете метод run напрямую, либо делаете что-то еще в первом методе, который его блокирует (например, thread.join ). Ни один из них не освободит поток swing. Первый метод ДОЛЖЕН возвращаться быстро, метод run() может занимать столько времени, сколько захочет.

Если вы выполняете поток.присоединяйтесь к первому методу, тогда поток НЕ возвращается в систему!

Редактировать: (Вторая правка на самом деле) Я думаю, чтобы поговорить о проблеме, которую вы на самом деле чувствуете — возможно, вам захочется больше думать в терминах системы модели / представления / контроллера. Код, который вы пишете, является контроллером (представление обычно считается компонентами на экране — представление / контроллер обычно очень тесно связаны).

Когда ваш контроллер получает событие, он должен передать работу вашей модели. После этого представление не отображается. Он не ждет модели, он просто сделан.

Когда ваша модель будет завершена, она должна затем сообщить контроллеру сделать что-то еще. Он делает это с помощью одного из методов invoke. Это передает управление обратно контроллеру, и вы продолжаете свой веселый путь. Если вы подумаете об этом с этой точки зрения, разделение контроля и преднамеренная передача его взад и вперед не кажутся такими громоздкими, и на самом деле это очень часто делается таким образом.

Комментарии:

1. В моем случае я выполнял thread.run() вместо thread.start(), и это привело меня к блокировке GUI. Установка thread.start() решила проблему.

Ответ №3:

Похоже, проблема может заключаться в том, что вы ожидаете завершения потоков внутри потока GUI. Ваш поток GUI не должен ожидать этих потоков, вместо этого вы должны заставить рабочие потоки вызывать некоторый метод в потоке GUI, который устанавливает флаг. Когда установлены оба флага, вы знаете, что оба потока завершены, и вы можете выполнить график.

Ответ №4:

Я не могу говорить о модели потоков swing, но:

Мне также нужно выполнить два этапа обработки по порядку, так каков наилучший способ убедиться, что второй ожидает первого?

Для такой функциональности я бы посоветовал вам создать два рабочих потока и внедрить JMS-брокер. Доставляйте работу двум потокам, передавая сообщения в очереди JMS, из которых они считывают. Ваш поток графического интерфейса может свободно просматривать очереди, чтобы определить, когда выполняется работа, и представлять состояние воспроизведения в вашем пользовательском интерфейсе.

Ответ №5:

Решением моей проблемы была смесь ответов jjnguy и Билла К., так что большое спасибо за это, ребята. Мне нужно было использовать потоки в SwingWorker следующим образом:

 public class Worker extends SwingWorker<Void, Void>   
{  
    private List<Object> list;  
    public YourClassSwingWorker(List<Object> theOriginalList){  
        list = theOriginalList;  
    }

    @Override
    public List<Object> doInBackground() {
        Thread t = new Thread(new ProcessorThread(list));  
        t.start();
    }

    @Override
    public void done() {
        // draw graph on GUI
    }
}  
class ProcessorThread implements Runnable {  
    //do lots of IO stuff  
    Thread t2 = new Thread(new SecondProcess());
    t2.start();  
}
 

Это гарантировало, что вся работа выполнялась рабочими потоками вдали от GUI, а также гарантировало, что сам SwingWorker не выполнял всю работу, что могло быть проблемой.

Комментарии:

1. Вау, вау, вау! Почему вы создаете поток из SwingWorker? SwingWorkers (при запуске с помощью метода execute() ) не выполняются в потоке GUI, поэтому не должно быть причин создавать здесь другой поток.

2. Должно быть, я изначально делал это неправильно, потому что это было единственное решение, которое сработало для меня