java SwingWorker запускает runnables из doInBackground() и как уведомить поток отправки событий

только что изучил SwingWorker и у меня есть вопрос
(я ищу ответ на этот вопрос, но конкретно не рассматриваю эту настройку)

Я создаю небольшой сервер, который будет иметь максимум 2-3 одновременных соединения.
Я использую Jframe с внутренним классом SwingWorker

В SwingWorker doInBackground() у меня есть:

while(true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  Future<Connection> future = es.submit(con , con ); 
  tasks.add(future);
 }

Connection является runnable и объявлен как подкласс в SwingWorker.

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

что лучше сделать:

1 - создать интерфейс, в котором все исполняемые файлы могут отправлять сообщения в поток отправки событий Jframe.

2 - использовать SwingWorker вместо runnables для всех новых подключений и Done() вызывать метод на сервере SwingWorker, который вызывает метод в Jframe, используя EventQueue.invokeLater..

3 - или использовать PropertyChangeListener (как-то не уверен)

4 - Пусть каждый runnables имеет ссылку s на Jframe и делает EventQueue.invokeLater..


person Erik    schedule 24.11.2011    source источник


Ответы (3)


Документы SwingWorker довольно ясны. Вы должны создать подкласс SwingWorker и выполнить длинную задачу в методе doInBackground(). Вы должны обновить пользовательский интерфейс в методе done().

Это действительно так просто.

Изменить:
чтобы было понятнее. Предполагая, что ваш класс Connection расширяет SwingWorker, ему нет необходимости реализовывать Runnable, и вам не нужно явно предоставлять пул потоков для запуска рабочих процессов. Просто поместите содержимое run() метод в doInBackground().

Теперь ваш основной цикл выглядит примерно так:

while (true) {
  Socket client_socket = listen_socket.accept();
  Connection con = new Connection(client_socket, "name");
  con.execute();
}

Кажется, вы подчиняетесь ExecutorService в своем основном цикле. Есть ли для этого конкретная причина (обратите внимание, что SwingWorker управляет собственным внутренним ThreadPoolExecutor для рабочих потоков). Это для ограничения количества одновременных клиентов? Если это так, есть другие способы сделать это.

person Qwerky    schedule 24.11.2011
comment
Мой сервер SwingWorker использует «done()», когда сервер закрывается. Как насчет всех «runnables», которые я запускаю из «doInBackground()», у них нет механизмов «done()» - person Erik; 24.11.2011
comment
Я думаю, что он уже знает об использовании SwingWorker. Его проблема находится на шаг впереди основ использования SwingWorker. - person Hovercraft Full Of Eels; 24.11.2011
comment
@Erik: Это Runnables или Futures? Если последнее, то у них есть своего рода метод done, особенно если это FutureTask или что-то подобное. - person Hovercraft Full Of Eels; 24.11.2011
comment
Прямо сейчас они являются фьючерсами только для тестирования. у меня нет возможности проверить фьючерсы на результат завершения, так как сервер SwingWorker блокирует listen_socket.accept(); (может я ошибаюсь) - person Erik; 24.11.2011
comment
@Erik Я вижу две потенциальные проблемы: 1) открытие соединения должно быть отдельным потоком или SwingWorker, если с успехом, то начните помещать / получать 2) не уверен, правильно ли определять одно Future‹Connection› внутри другого SwingWorker‹Future›, но может быть возможно объединение двух фьючерсов, но для реализации Propert&ChangeListener необходимо прочитать stackoverflow.com/q/7054627/714968 - person mKorbel; 24.11.2011
comment
Смотрите мое обновление. Дайте вашему серверному классу синхронизированный метод, который обновляет графический интерфейс с результатами, передайте ссылку экземпляру сервера при создании своих клиентских рабочих процессов Swing, пусть клиентские рабочие процессы Swing вызывают метод GUI обновления сервера из done(). - person Qwerky; 24.11.2011
comment
@Qwerky звучит многообещающе, сейчас попробую. Одна мысль касается сервера listen_socket.accept();. не заблокирует ли это метод синхронизации реф-сервера? - person Erik; 24.11.2011
comment
@Erik - Нет, метод синхронизированного сервера вызывается рабочими потоками Swing, а не основным потоком сервера (который будет тратить 99,9% своего времени на блокировку listen_socket.accept()) - person Qwerky; 24.11.2011

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

person GETah    schedule 24.11.2011
comment
может работать. parent в моем случае — это «SwingWorker», и он уже блокирует «doInBackground () listen_socket.accept ()». Можно запустить поток прослушивания в родительском элементе, содержащем изменчивый список очереди блокировки, и он будет отправлять сообщения в «Jframe», используя EventQueue.invokeLater. Я не эксперт, и это кажется излишним - person Erik; 24.11.2011
comment
Ага, я лично использую этот производитель/потребитель в большой распределенной системе и пока не видел с ним проблем - person GETah; 24.11.2011

Я попытался создать пример SwingWorker, который генерирует фоновые рабочие потоки, результаты которых публикуются через фиксированный пул ExecutorService и CompletionService. У меня все еще есть некоторые вопросы, касающиеся безопасности потоков при создании рабочих потоков внутри одного потока и последующем вызове их фьючерсов внутри другого потока (фоновый поток SwingWorker).

Например:

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.*;

@SuppressWarnings("serial")
public class TestSwingWorker extends JPanel {
   public static final int POOL_SIZE = 4;
   private JTextArea tArea = new JTextArea(10, 30);
   private JButton doItBtn;

   public TestSwingWorker() {
      doItBtn = new JButton(new AbstractAction("Do It!") {
         public void actionPerformed(ActionEvent ae) {
            swingWorkerRunning(true);
            MySwingWorker mySW = new MySwingWorker();
            mySW.execute();
            tArea.append("SwingWorker started\n");
         }
      });
      JPanel btnPanel = new JPanel();
      btnPanel.add(doItBtn);
      tArea.setEditable(false);
      tArea.setFocusable(false);

      setLayout(new BorderLayout());
      add(new JScrollPane(tArea), BorderLayout.CENTER);
      add(btnPanel, BorderLayout.SOUTH);
   }

   private class MySwingWorker extends SwingWorker<String, String> {
      @Override
      protected String doInBackground() throws Exception {
         ExecutorService execService = Executors.newFixedThreadPool(POOL_SIZE);
         final CompletionService<String> completionService = new ExecutorCompletionService<String>(
               execService);
         new Thread(new Runnable() {

            @Override
            public void run() {
               for (int i = 0; i < POOL_SIZE; i++) {
                  final int index = i;
                  completionService.submit(new Callable<String>() {
                     public String call() throws Exception {
                        Thread.sleep(2000 * index + 500);
                        return "Callable " + index + " complete";
                     }
                  });
                  try {
                     Thread.sleep(1000);
                  } catch (InterruptedException e) {
                  }
               }
            }
         }).start();

         for (int i = 0; i < POOL_SIZE; i++) {
            Future<String> f = completionService.take();
            publish(f.get());
         }

         return "Do in background done";
      }

      @Override
      protected void process(List<String> chunks) {
         for (String chunk : chunks) {
            tArea.append(chunk + "\n");
         }
      }

      @Override
      protected void done() {
         try {
            tArea.append(get() + "\n");
         } catch (InterruptedException e) {
            e.printStackTrace();
         } catch (ExecutionException e) {
            e.printStackTrace();
         } finally {
            swingWorkerRunning(false);
         }
      }
   }

   public void swingWorkerRunning(boolean running) {
      doItBtn.setEnabled(!running);
   }

   private static void createAndShowGui() {
      TestSwingWorker mainPanel = new TestSwingWorker();

      JFrame frame = new JFrame("TestSwingWorker");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Поправки приветствуются!

person Hovercraft Full Of Eels    schedule 24.11.2011