Предотвращение блокировки графического интерфейса Swing во время фоновой задачи - PullRequest
11 голосов
/ 02 июня 2009

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

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

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

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

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

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

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

Ответы [ 5 ]

18 голосов
/ 02 июня 2009

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

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

Некоторые распространенные способы сделать это - использовать таймеры или 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 и скажите, чтобы он запускался.

6 голосов
/ 02 июня 2009

Вы не возвращаете нить качания должным образом. Я понимаю, что вы используете 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 в первом методе, то поток НЕ возвращается в систему!

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

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

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

1 голос
/ 02 июня 2009

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

0 голосов
/ 04 июня 2009

Решением моей проблемы была смесь ответов 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();  
}

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

0 голосов
/ 02 июня 2009

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

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

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

...