SwingWorker publish()/process() действует как done()

Детали реализации: я работаю над школьным проектом, в котором мне нужно имитировать некоторые очереди. Через случайные промежутки времени должны генерироваться клиенты, клиент выбирает одну очередь (у меня может быть несколько очередей) для входа и добавляется в эту структуру данных очереди. У каждой очереди есть свой оператор, который удаляет клиентов из очереди, к которой она присоединена.

Проблема: Генератор клиентов запускается в отдельном потоке. Графическое представление очереди представляет собой ArrayList из JButtons, отображаемый на панели GridLayout только с 1 столбцом. Когда я пытаюсь добавить клиент (JButton) на панель, я хочу использовать SwingWorker publish() для публикации нового JButton, который будет добавлен в список. Однако, после многих головных болей и System.out.println, чтобы выяснить, что происходит, я заметил, что System.out.println() в методе process() вызывается только после завершения метода doBackground().

Код здесь:

  //run method of the ClientGenerator thread
  public void run()
  {

      System.out.println("Into thread Generator");
      SwingWorker<Void,JButton> worker=new SwingWorker<Void, JButton>()
      {
          int sleepTime;
          @Override
          protected Void doInBackground() throws Exception
          {

              while(checkTime())
              {
                  try
                  {
                      sleepTime=minInterval+r.nextInt(maxInterval - minInterval);
                      System.out.println("Sleeping - "+sleepTime+" milis");
                      Thread.sleep(sleepTime);
                      System.out.println("Woke up,"+sleepTime+" milis elapsed");

                  } catch (InterruptedException e)
                  {
                      e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                  }
                  System.out.println("Generating client...");
                  newClient=new Client(clientMinService,clientMaxService,log);
                  System.out.println("Locking lock...");
                  operationsOnTheQueueLock.lock();
                  selectedQueueOperator=selectQueueOperator();
                  System.out.println("Adding new client to queue...");
                  selectedQueueOperator.getQueue().enqueue(newClient);
                  System.out.println("Publishing new JButton...");
                  publish(new JButton("C"+selectedQueueOperator.getClientIndicator()));
                  //}
                  // else
                  //  {
                  //     queueHolder.add(selectedQueueOperator.getQueueClients().get(0);
                  // }

                  System.out.println("Unlocking lock...");
                  operationsOnTheQueueLock.unlock();
                  System.out.println("Lock unlocked! Should enter while again and sleep");

              }
          return null;
          }
          @Override
          public void process(List<JButton> chunks)
          {


                  newClientButton=chunks.get(chunks.size()-1);

                  System.out.println("Process runs.Jbutton index="+newClientButton.getText());
                  newClientButton.setFont(new Font("Arial", Font.PLAIN, 10));
                  newClientButton.setBackground(Color.lightGray);
                  newClientButton.setVisible(true);
                  newClientButton.setEnabled(false);
                  clients=selectedQueueOperator.getQueueClients();
                  clients.add(newClientButton);
                  selectedQueueOperator.setQueueClients(clients);
                  //       if(selectedQueueOperator.getQueueClients().size()>0)
              //   {

                  queueHolder=selectedQueueOperator.getQueueHolder();
                  queueHolder.add(clients.get(clients.size()-1));
                  selectedQueueOperator.setQueueHolder(queueHolder);
          }


           //   return null;  //To change body of implemented methods use File | Settings | File Templates.
      };
      worker.execute();
      try {
          worker.get();
      } catch (InterruptedException e) {
          e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      } catch (ExecutionException e) {
          e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
      }
  }

выход:

Sleeping - 1260 milis
Woke up,1260 milis elapsed
Generating client...
Locking lock...
Adding new client to queue...
Publishing new JButton... ///here I should see "Process runs.Jbutton index=C0"
Unlocking lock...
Lock unlocked! Should enter while again and sleep
Sleeping - 1901 milis
Woke up,1901 milis elapsed
Generating client...
Locking lock...
Adding new client to queue...
Publishing new JButton...///here I should see "Process runs.Jbutton index=C1
Unlocking lock...
Lock unlocked! Should enter while again and sleep
Process runs.Jbutton index=C0  //instead, Process runs only in the end.

Это просто базовый пример для 2 итераций. Клиенты должны генерироваться только время от времени, поэтому в начале я сплю поток на определенное время. Затем я создаю клиентский объект, затем я хочу создать и добавить кнопку в свой компонент JPanel в методе process().

Эта последняя часть, очевидно, не происходит. Любые идеи, почему? Мне нечего попробовать, что касается SwingWorker...

Заранее спасибо!

Позднее редактирование: «замок» определяется как:

Lock lock = new ReentrantLock();

и передается как параметр из класса, который управлял моим классом ClientsGenerator(this), и моим классом, который удаляет клиентов из очереди. Он используется для их синхронизации при выполнении операций над дисплеем ArrayList&.


person cjurjiu    schedule 28.03.2013    source источник
comment
Почему вы добавляете JButtons как часть публикации ()? Определите JList и добавьте элементы в DefaultListModel для публикации () и удалите элементы из DefaultListModel для обработки ().   -  person Gilbert Le Blanc    schedule 28.03.2013
comment
просто для акцента: создание JButton в doInBackground неправильно (как на самом деле очень неправильно)   -  person kleopatra    schedule 28.03.2013
comment
@kleopatra хорошо, я много чего перепробовал, прежде чем опубликовать это здесь. создание объекта jbutton в методе процесса и передача строки в публикацию также не сработали. передача нового экземпляра jbutton через публикацию была последним, что я пробовал, прежде чем публиковать его здесь: с   -  person cjurjiu    schedule 28.03.2013
comment
Попытки неправильных вещей - это тупик, даже если вы в отчаянии :-) В любом случае, рад, что @Walter смог вам помочь   -  person kleopatra    schedule 28.03.2013


Ответы (2)


Весь смысл потоков в том, что вещи не выполняются последовательно. doInBackground() может завершиться (итерация цикла while) до вызова process(). doInBackground() запускается в рабочем потоке Swing. process() запускается в EDT.

process() запустится перед done() (поскольку он также запускается в EDT).

Как отмечено в другом ответе: вы должны опубликовать только текст, а затем создать JButton в процессе().

Обратите внимание, что обычно вы запускаете SwingWorker из EDT, в этом случае вам не следует вызывать get() в EDT (что заблокирует его).

Простой пример:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;

public class SwingWorkerTest {
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JPanel panel = new JPanel(new GridLayout(0, 1));
                new SwingWorker<Void, String>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        Random random = new Random();
                        int count = 1;
                        while (count < 100) {
                            publish("Button " + (count++));
                            Thread.sleep(random.nextInt(1000) + 500);
                        }
                        return null;
                    }

                    @Override
                    protected void process(List<String> chunks) {
                        for (String text : chunks) {
                            panel.add(new JButton(new AbstractAction(text) {
                                @Override
                                public void actionPerformed(ActionEvent e) {
                                    panel.remove((JButton) e.getSource());
                                    panel.revalidate();
                                    panel.repaint();
                                }
                            }));
                        }
                        panel.revalidate();
                        panel.repaint();
                    }
                }.execute();

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.getContentPane().add(new JScrollPane(panel));
                frame.setPreferredSize(new Dimension(400, 300));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
}
person Walter Laan    schedule 28.03.2013
comment
Обратите внимание, что обычно вы запускаете SwingWorker из EDT, в этом случае вам не следует вызывать get() в EDT (что заблокирует его). ‹---- Это была моя проблема. Удалил все свои заявления, оставил только sysouts, проблема осталась. Я использовал get(), чтобы заблокировать поток, в котором я вызвал worker.execute, от завершения (поскольку worker.execute() возвращается немедленно). Мне это нужно, потому что в теле моей основной программы есть функция .join(), которая ожидает завершения потока с обработчиком размаха. - person cjurjiu; 28.03.2013
comment
Если этот поток завершался немедленно, выполнение моей программы также завершалось, даже несмотря на то, что рабочий процесс свинга все еще работал в фоновом режиме. С примером, который вы написали, теперь я знаю, как решить эту проблему. Спасибо! - person cjurjiu; 28.03.2013

Вы не должны размещать или получать доступ к компонентам пользовательского интерфейса в doInBackground(). Все взаимодействия с пользовательским интерфейсом должны выполняться в потоке отправки событий. Сделайте это в done() или process(), которые выполняются по EDT. См. раздел Параллелизм в Swing для получения подробной информации об однопоточном характере Swing.

Также есть опасная игра с operationsOnTheQueueLock блокировкой. Возможно, вы блокируете ветку. Пожалуйста, рассмотрите возможность размещения всего соответствующего кода в качестве рабочего образца, т. е. SSCCE.

См. документы SwingWorker. пример использования методов publish()/process().

person tenorsax    schedule 28.03.2013
comment
Это то, что я пытаюсь сделать. Я пытаюсь добавить JButtons в JPanel в методе process(), но по какой-то причине процесс не вызывается, когда я вызываю публикацию. - person cjurjiu; 28.03.2013
comment
@ppsi В опубликованном коде вы выделяете JButton в doInBackground(). Это также может привести к непредсказуемым результатам. Тоже непонятно где второй замок? - person tenorsax; 28.03.2013
comment
вторая блокировка используется, когда я вызываю dequeue в классе, который содержит очередь, и удаляет из нее элементы. он делает что-то вроде lock.lock; dequeue(); lock.unlock; Я изменил это publish(new JButton("C"+selectedQueueOperator.getClientIndicator())) на publish(a_Button_That_I_don't_Use);, которое было объявлено ранее. все еще не работает :‹ Я попробую SSCCE! - person cjurjiu; 28.03.2013