Почему после использования многопоточного приложения мой GUI по-прежнему зависает? - PullRequest
1 голос
/ 02 января 2012

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

для получения данных из Интернета я использую следующий класс:

public class WebReader implements Runnable {
    WebRecordResult WRResult = new WebRecordResult ();
    private void    getData(args){
        .
        .
        .
        setResult(result);
    }

    public void run() {
        getData(args);
        WRResult.setResult(getResult());
    }

}


public class WebReaderResult {
    private AtomicReference<String> result = new AtomicReference<>("");

    public String getResult() {
        return result.get();
    }

    public void setResult(String s) {
        result.set(s);
    }
}

и внутри моего контроллера у меня естьсоздал что-то вроде:

WebReaderResult wrr = new WebReaderResult();

ExecutorService executor = Executors.newSingleThreadExecutor();
WebReader wr =new WebReader(args);
Future<?> f1 = executor.submit(wr);
executor.shutdown();
try {
    f1.get();
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
wrr.getResult(); 

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

Примечание: мой графический интерфейс - свинг

Ответы [ 5 ]

4 голосов
/ 02 января 2012

Линия

f1.get();

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

** РЕДАКТИРОВАТЬ **

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

Результаты

public class WebReaderResult<V> {
   private AtomicReference<V> ref = new AtomicReference<V>();

   public V getResult() { return ref.get(); }
   public void setResult(V value) { ref.set(value); }
}

Задача

public abstract class WebTask<V> {
   private WebReaderResult<V> res = new WebReaderResult<V>();

   public WebReaderResult<V> getWebReaderResult() { return res; }

   // handle task here
   public abstract void handle();
}

Контроллер

public interface WebControllerCallback<V> {
   public void done(V result);
}

public class WebController {
   static private WebController instance;
   static public WebController getInstance() {
      if (null == instance) instance = new WebController();
      return instance;
   }

   private ExecutorService executor = Executors.newSingleThreadExecutor();

   public void handleTask(WebTask<?> t, WebControllerCallback<?> cb) {
      executor.submit(new Runnable() {
         public void run() {
            t.handle();
            cb.done(t.getWebReaderResult());
         }
      });
   }

   // other methods here
}

Код интерфейса пользователя

(в вашем ActionListener или любом другом методе события пользовательского интерфейса)

WebTask<String> task = new WebTask<String>() {
   public void handle() {
      WebReaderResult<String> result = getWebReaderResult();

      // TODO : handle task and set result here result.getResult();
   }
};
WebControllerCallback<String> callback = new WebControllerCallback<String>() {
   public void done(String result) {
      // TODO : update UI here from result value
   }
};

WebController.getInstance().handleTask(task, callback);

** РЕДАКТИРОВАТЬ 2 **

Как уже упоминали другие пользователи, начиная с Java 1.6 существует класс под названием SwingWorker, который вы можете использовать именно для этого, но с гораздо меньшим количеством кода. Просто поместите что-то подобное в любое место вашего события пользовательского интерфейса (или создайте отдельный класс для реализации методов):

new SwingWorker<String, Object>() {
   @Override
   protected String doInBackground() throws Exception {
      // TODO : process result

      return "some result";
   }

   protected void done() {
      // TODO : refresh UI with the value of this.get(); which return a String
   }
}.execute();

Последнее редактирование не так понятно, как в первом примере, однако класс SwingWorker предлагает то, что вы ищете. Теперь все, что вам нужно, это делегировать обработку, выполняемую как в doInBackground, так и done, используя ваш класс WebReader (то есть, вызывая wr.run(); в doInBackground или что-то в этом роде). В любом случае, я полагаю, вам есть над чем поработать.

3 голосов
/ 02 января 2012

Качели однопоточные.Имя этого потока - Поток отправки событий (EDT) .Приложение Swing будет зависать при длительном выполнении задачи в EDT.Чтобы избежать этого, были разработаны такие утилиты, как javax.swing.Timer, javax.swing.SwingUtilities и javax.swing.SwingWorker<T,V>.Эти служебные классы гарантируют, что любое действие, которое изменяет компонент Swing, происходит в EDT и что все долгосрочные задачи выполняются в отдельном потоке (так называемый фоновый поток).

Я предлагаю вам просмотреть свой код и дополнительно выяснить, какие методы могут блокировать поток (это обычно документируется).После этого вы можете проверить, встречаются ли эти методы в EDT, вызывая SwingUtilities.isEventDispatchThread() до их выполнения (это только для проверки).

1 голос
/ 02 января 2012

что мне делать?

Прочитайте документацию. :)

Серьезно:

JavaDoc на Future#get() говорит следующее (выделено мной):

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

Таким образом, вы можете пытаться вызывать f1.isDone() каждый сейчас и так часто, пока он не вернет true, и в этом случае вы можете позвонить get().

0 голосов
/ 02 января 2012

Прямо из официальных javadocs (с некоторыми полезными комментариями):

final JLabel label;
   class MeaningOfLifeFinder extends SwingWorker<String, Object> {
       @Override
       public String doInBackground() {
           // In here put the time consuming task
           return findTheMeaningOfLife();
       }

       @Override
       protected void done() {
           // In here you'd be in UI thread, so you can actually render some text in a JLabel
           try { 
               label.setText(get());
           } catch (Exception ignore) {
           }
       }
   }

И, наконец, вот использование:

   (new MeaningOfLifeFinder()).execute();
0 голосов
/ 02 января 2012

Код f1.get () является блокирующим вызовом.

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

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

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