Ожидание нескольких SwingWorkers

Обратите внимание на следующий фрагмент кода:

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import javax.swing.*;

public class TestApplet extends JApplet
{
    @Override
    public void init()
    {
        try
        {
            SwingUtilities.invokeAndWait(new Runnable()
            {
                @Override
                public void run()
                {
                    createGUI();
                }
            });
        }
        catch(InterruptedException | InvocationTargetException ex)
        {
        }
    }

    private void createGUI()
    {
        getContentPane().setLayout(new FlowLayout());
        JButton startButton = new JButton("Do work");
        startButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent ae)
            {
                JLabel label = new JLabel();
                new Worker(label).execute();
            }
        });
        getContentPane().add(startButton);
    }

    private class Worker extends SwingWorker<Void, Void>
    {
        JLabel label;

        public Worker(JLabel label)
        {
            this.label = label;
        }

        @Override
        protected Void doInBackground() throws Exception
        {
            // do work
            return null;
        }

        @Override
        protected void done()
        {
            getContentPane().remove(label);
            getContentPane().revalidate();
        }
    }
}

Вот добавление метки к апплету, которая отображает некоторые промежуточные результаты рабочего потока (с использованием методов публикации / обработки). В конце метка удаляется с панели апплета. Мой вопрос в том, как я могу создать несколько ярлыков, каждая со своим собственным потоком Worker, и удалить их, когда все они будут готовы?

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

ОБНОВЛЕНИЕ:

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

ОБНОВЛЕНИЕ 2:

Следующий код, кажется, делает то, что мне нужно. Прокомментируйте, правильно ли я все сделал. У меня такое чувство, что что-то не так. Одна из проблем заключается в том, что метки справа от кнопки остаются видимыми, хотя и удаляются. setVisible (false), похоже, решает эту проблему. Это способ сделать это?

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;

public class TestApplet extends JApplet
{
    private Queue<JLabel> labels = new LinkedList<>();
    private static final Random rand = new Random();

    @Override
    public void init()
    {
        try
        {
            SwingUtilities.invokeAndWait(new Runnable()
            {
                @Override
                public void run()
                {
                    createGUI();
                }
            });
        }
        catch(InterruptedException | InvocationTargetException ex){}
    }

    private void createGUI()
    {
        getContentPane().setLayout(new FlowLayout());
        JButton startButton = new JButton("Do work");
        startButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent ae)
            {
                ExecutorService executor = Executors.newFixedThreadPool(10);
                for(int i = 0; i < 10; i++)
                {
                    JLabel label = new JLabel();
                    getContentPane().add(label);
                    executor.execute(new Counter(label));
                }
            }
        });
        getContentPane().add(startButton);
    }

    private class Counter extends SwingWorker<Void, Integer>
    {
        private JLabel label;

        public Counter(JLabel label)
        {
            this.label = label;
        }

        @Override
        protected Void doInBackground() throws Exception
        {
            for(int i = 1; i <= 100; i++)
            {
                publish(i);
                Thread.sleep(rand.nextInt(80));
            }

            return null;
        }

        @Override
        protected void process(List<Integer> values)
        {
            label.setText(values.get(values.size() - 1).toString());
        }

        @Override
        protected void done()
        {
            labels.add(label);

            if(labels.size() == 10)
            {
                while(!labels.isEmpty())
                    getContentPane().remove(labels.poll());

                getContentPane().revalidate();
            }
        }
    }
}

person Vlad    schedule 06.07.2012    source источник
comment
Вы также можете включить их в список (JList).   -  person Andrew Thompson    schedule 06.07.2012
comment
@AndrewThompson, Ага, а где мне разместить код, который их будет ждать? Должен ли это быть еще один SwingWorker, порождающий несколько других?   -  person Vlad    schedule 06.07.2012
comment
Пожалуйста, не проглатывайте исключения.   -  person trashgod    schedule 07.07.2012
comment
@trashgod Спасибо. Определенно. Это всего лишь пример.   -  person Vlad    schedule 07.07.2012


Ответы (2)


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

Как описано здесь, CountDownLatch хорошо работает в этом контексте. В приведенном ниже примере каждый воркер вызывает latch.countDown() по завершении, а Supervisor воркер блокируется latch.await() до завершения всех задач. В демонстрационных целях Supervisor обновляет этикетки. Полное удаление, указанное в комментариях, технически возможно, но в целом непривлекательно. Вместо этого рассмотрите JList или JTable.

Тест фиксации рабочего

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.*;

/**
* @see https://stackoverflow.com/a/11372932/230513
* @see https://stackoverflow.com/a/3588523/230513
*/
public class WorkerLatchTest extends JApplet {

    private static final int N = 8;
    private static final Random rand = new Random();
    private Queue<JLabel> labels = new LinkedList<JLabel>();
    private JPanel panel = new JPanel(new GridLayout(0, 1));
    private JButton startButton = new JButton(new StartAction("Do work"));

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.setTitle("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new WorkerLatchTest().createGUI());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    @Override
    public void init() {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                add(new WorkerLatchTest().createGUI());
            }
        });
    }

    private JPanel createGUI() {
        for (int i = 0; i < N; i++) {
            JLabel label = new JLabel("0", JLabel.CENTER);
            label.setOpaque(true);
            panel.add(label);
            labels.add(label);
        }
        panel.add(startButton);
        return panel;
    }

    private class StartAction extends AbstractAction {

        private StartAction(String name) {
            super(name);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
                startButton.setEnabled(false);
                CountDownLatch latch = new CountDownLatch(N);
                ExecutorService executor = Executors.newFixedThreadPool(N);
                for (JLabel label : labels) {
                    label.setBackground(Color.white);
                    executor.execute(new Counter(label, latch));
                }
                new Supervisor(latch).execute();
        }
    }

    private class Supervisor extends SwingWorker<Void, Void> {

        CountDownLatch latch;

        public Supervisor(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        protected Void doInBackground() throws Exception {
            latch.await();
            return null;
        }

        @Override
        protected void done() {
            for (JLabel label : labels) {
                label.setText("Fin!");
                label.setBackground(Color.lightGray);
            }
            startButton.setEnabled(true);
            //panel.removeAll(); panel.revalidate(); panel.repaint();
        }
    }

    private static class Counter extends SwingWorker<Void, Integer> {

        private JLabel label;
        CountDownLatch latch;

        public Counter(JLabel label, CountDownLatch latch) {
            this.label = label;
            this.latch = latch;
        }

        @Override
        protected Void doInBackground() throws Exception {
            int latency = rand.nextInt(42) + 10;
            for (int i = 1; i <= 100; i++) {
                publish(i);
                Thread.sleep(latency);
            }
            return null;
        }

        @Override
        protected void process(List<Integer> values) {
            label.setText(values.get(values.size() - 1).toString());
        }

        @Override
        protected void done() {
            label.setBackground(Color.green);
            latch.countDown();
        }
    }
}
person trashgod    schedule 07.07.2012
comment
Пожалуйста; такой гибридный апплет / приложение предлагает множество вариантов развертывания с использованием java-web-start, как показано здесь. - person trashgod; 07.07.2012
comment
+1 отличный ответ и пример кода! (придирки, не связанные с сутью вопроса: использование ActionListener, кашель :-) - person kleopatra; 07.07.2012
comment
@kleopatra: Спасибо за напоминание; Action более гибкий. - person trashgod; 07.07.2012
comment
Не могли бы вы подробнее рассказать о преимуществах Action вместо ActionListener в этом случае? - person Vlad; 07.07.2012
comment
Да, но не так хорошо, как соответствующее руководство. - person trashgod; 07.07.2012
comment
Ваш код мне очень помог, спасибо :). (PS: подразумевается +1 ...) - person Radu Murzea; 11.05.2013

Код, который у вас есть, в определенной степени уже это делает. Вам действительно нужно добавить метку в область содержимого при нажатии кнопки. Что-то вроде этого:

 JLabel label = new JLabel();
 getContentPane().add(label);
 getContentPane().validate();
 new Worker(label).execute();

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

 JLabel label = new JLabel("Hello...I am here");

И, наконец, в методе doInBackground () вы можете добавить код для обновления метки при выполнении некоторой задачи:

 for(int i = 0;i < 100; i++){
            Thread.sleep(20);
            label.setText("Counting..." + i);
  }

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

person Vincent Ramdhanie    schedule 06.07.2012
comment
Спасибо за быстрый ответ. Я упростил код, прежде чем вставить его сюда. Метка действительно имеет текст и добавляется на панель содержимого. :) Спасибо за предложения. Думаю, я не сформулировал свой вопрос четко. Я собираюсь удалить все метки вместе, когда все их рабочие выполнят задачи. - person Vlad; 06.07.2012