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

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

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

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

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

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

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

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

Ответ 1

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

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

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

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

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

Вот пример реализации 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 и скажите ему, чтобы он начинался.

Ответ 2

Вы не возвращаете качающуюся нить должным образом. Я понимаю, что вы используете 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). Ни один из них не освободит качающуюся нить. Первый метод ДОЛЖЕН вернуться быстро, метод run() может занять столько времени, сколько захочет.

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

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

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

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

Ответ 3

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

Ответ 4

Я не могу говорить с моделью качающейся резьбы, но:

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

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

Ответ 5

Решение моей проблемы представляло собой смесь ответов jjnguy и Bill K, поэтому большое спасибо тем парням. Мне нужно было использовать потоки в 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();  
}

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