使用 SwingWorker 在线程池中等待任务 java

Waiting for a task inside a thread pool using SwingWorker java

我在一个线程内模拟多个任务 - 当所有任务都应该向 GUI 报告时。所以我有一个独特的表单,里面有 4 个面板,它们应该回复并执行不同的任务.. 他们每个人都应该继续汇集一个数据库并向 GUI 报告内容。在此示例中,我只是将数字写入文本区域。

public class FormMain extends JFrame {
    private JButton btnStart;
    private JPanel _panelTop, _panelMid, _panelBot;

    private PanelFoo panelFooA, panelFooB, panelFooC, panelFooD;

    private final List<String> ugsRj = Arrays.asList("AB");
    private final List<String> ugsMg = Arrays.asList("CD", "EF");
    private final List<String> ugsBA = Arrays.asList("GH", "IJ", "KL");
    private final List<String> ugsPE = Arrays.asList("MN", "OP", "RS", "TU");

    private void initialize() {
        this._panelTop = new JPanel();
        this._panelMid = new JPanel();
        this._panelBot = new JPanel();

        this.btnStart = new JButton("Start");
        this._panelBot.add(this.btnStart);

        this.panelFooA = new PanelFoo(this.ugsRj);
        this.panelFooB = new PanelFoo(this.ugsMg);
        this.panelFooC = new PanelFoo(this.ugsBA);
        this.panelFooD = new PanelFoo(this.ugsPE);
        _panelMid.setLayout(new BoxLayout(_panelMid, BoxLayout.X_AXIS));

        this._panelMid.add(this.panelFooA);
        this._panelMid.add(this.panelFooB);
        this._panelMid.add(this.panelFooC);
        this._panelMid.add(this.panelFooD);

    }

    public FormMain() {
        initialize();
        getContentPane().setLayout(new BorderLayout());
        setSize(800, 516);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        getContentPane().add(this._panelTop, BorderLayout.NORTH);
        getContentPane().add(this._panelMid, BorderLayout.CENTER);
        getContentPane().add(this._panelBot, BorderLayout.SOUTH);

        this.btnStart.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                final AuthenticationUser auth = new AuthenticationUser();
                auth.setUser("test");
                auth.setPassword("p@ss");
//
                final SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
                        w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
                        final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
                        w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
                        final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
                        w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
                        final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
                        w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

                        w1.execute();
                        w2.execute();
                        w3.execute();
                        w4.execute();

                        return null;
                    }

                };
                worker.execute();
            }
        });

    }
}

public class ProgressListener implements PropertyChangeListener {
    private JProgressBar bar;

    ProgressListener() {
    }

    ProgressListener(final JProgressBar b) {
        this.bar = b;
        this.bar.setValue(0);
    }

    @Override
    public void propertyChange(final PropertyChangeEvent evt) {
        // Determine whether the property is progress type
        if ("progress".equals(evt.getPropertyName())) {
            this.bar.setValue((int) evt.getNewValue());
        }
    }
}

public class PanelFoo extends JPanel {

    /**
     * 
     */
    private static final long serialVersionUID = -1400188281877395934L;
    private JLabel label;
    private JTextArea textArea;
    private JScrollPane scrollPanel;
    private JProgressBar progressBar;

    public PanelFoo(final List<String> listOfStates) {
        setLayout(new FlowLayout());
        setSize(180, 400);
        final ImageIcon icon = createImageIcon("/images/waiting-list.png", "waiting start");
        this.label = new JLabel(listOfStates.get(0), icon, SwingConstants.HORIZONTAL);
        add(this.label);

        this.textArea = new JTextArea("Numbers: \n");
        this.textArea.setWrapStyleWord(true);
        this.scrollPanel = new JScrollPane(this.textArea);
        this.scrollPanel.setPreferredSize(new Dimension(150, 350));

        this.progressBar = new JProgressBar(0, 100);

        add(this.scrollPanel);
        add(this.progressBar);

        setVisible(true);
    }

    /** Returns an ImageIcon, or null if the path was invalid. */
    public ImageIcon createImageIcon(final String path, final String description) {
        if (path != null) {
            ImageIcon imageIcon = new ImageIcon(getClass().getResource(path));
            final Image image = imageIcon.getImage(); 
            final Image newimg = image.getScaledInstance(30, 30, Image.SCALE_SMOOTH);
            imageIcon = new ImageIcon(newimg, description);
            return imageIcon;

        } else {
            System.err.println("Couldn't find file: " + path);
            return null;
        }
    }

    public final JLabel getLabel() {
        return this.label;
    }

    public final void setLabel(final JLabel label) {
        this.label = label;
    }

    public final JTextArea getTextArea() {
        return this.textArea;
    }

    public final void setTextArea(final JTextArea textArea) {
        this.textArea = textArea;
    }

    public final JProgressBar getProgressBar() {
        return this.progressBar;
    }

    public final void setProgressBar(final JProgressBar progressBar) {
        this.progressBar = progressBar;
    }

}

public class WorkerDoSomething extends SwingWorker<Void, Void> {

    private JTextArea txtArea;
    private JLabel label;
    private Random r = new Random();

    WorkerDoSomething() {
    }

    public WorkerDoSomething(final PanelFoo panelFooInstance) {
        this.txtArea = panelFooInstance.getTextArea();
        this.label = panelFooInstance.getLabel();
    }

    private Integer randomInt(final int min, final int max) {
        final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;

        return randomNumber;
    }

    @Override
    protected Void doInBackground() throws Exception {
        final Integer randomNumber = randomInt(10000000, 1000000000);
        long j;
        int progress = 0;
        final int onePerCent = randomNumber / 100;
        final int onePerMillion = onePerCent / 10;
        for (j = 0; j <= randomNumber; j++) {
            if (j % onePerCent == 0) {
                progress = (int) j / onePerCent;
                setProgress(progress);
            }
            if (j % onePerMillion == 0) {
                publish(j);
            }
            //Thread.sleep(randomInt(1000, 5000));
        }
        

        return null;
    }

    private void publish(final long num) {
        this.txtArea.append(num + "\n");
        this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
    }

}

毕竟这是主要的 GUI:

这是执行:

我只需要在每个 WorkerDoSomething 的任务上等待一段时间,添加该行(之前注释掉):Thread.sleep(randomInt(1000, 5000));

但是当我这样做时..整个执行冻结,当然..因为它使用单个线程来 运行 所有任务 - 我想。

有解决办法吗?

哦...我在业务中必须使用java 1.8:

java version "1.8.0_251"
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

整个项目都是我个人的git

--- 首先使用调试视角编辑

执行不会冻结,因为 Swing 使用 10 个线程的线程池 运行 工作人员。
注释掉这部分就可以正常运行了:

if (j % onePerMillion == 0) {
    publish(j);
}

PS - 为什么要在actionPerformed方法中新建一个SwingWorker?
为什么不简单地这样写:

    this.btnStart.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
        w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
        final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
        w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
        final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
        w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
        final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
        w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

        w1.execute();
        w2.execute();
        w3.execute();
        w4.execute();
      }
    });

您是否阅读了 javadoc class SwingWorker?以下是摘录:

Class SwingWorker<T,V>
Type Parameters:
T - the result type returned by this SwingWorker's doInBackground and get methods
V - the type used for carrying out intermediate results by this SwingWorker's publish and process methods

您在 doInBackground() 方法中正确调用了方法 publish(),但您没有覆盖方法 process()。参考Tasks that Have Interim Results。假设你的 publish() 方法真的应该是 process() 方法,那么这应该是 class WorkerDoSomething

的声明
public class WorkerDoSomething extends SwingWorker<Void, Long> {

这应该是方法 process()(而不是方法 publish()

protected void process(List<Long> nums) {
    this.txtArea.append(nums.get(nums.size() - 1) + "\n");
    this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
}

但是那样做并不能解决你的问题。我无法解释原因,但如果您在方法 doInBackground() 中更改对 Thread.sleep() 的调用位置,问题就会解决。如果我在调用 setProgress() 之前立即调用方法 sleep(),则 GUI 不会“冻结”。我想到了将调用位置从 this 网页移动到睡眠状态,标题为 Java Swing How to - Put longly task in SwingWorker

这是我对您的应用程序的重写。我冒昧地重写了其中的一部分,但本质上你只需要注意 WorkerDoSomething class.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;

public class FormMain implements Runnable, ActionListener {
    private static final int  PANELS = 4;
    private static final String  START = "Start";

    private JFrame  frame;
    private PanelFoo[]  fooPanels;

    public FormMain() {
        fooPanels = new PanelFoo[PANELS];
        for (int i = 0; i < PANELS; i++) {
            fooPanels[i] = new PanelFoo();
        }
    }

    @Override // java.awt.event.ActionListener
    public void actionPerformed(ActionEvent event) {
        String actionCommand = event.getActionCommand();
        switch (actionCommand) {
            case START:
                start();
                break;
            default:
                JOptionPane.showMessageDialog(frame,
                                              actionCommand,
                                              "Unhandled",
                                              JOptionPane.WARNING_MESSAGE);
        }
    }

    @Override // java.lang.Runnable
    public void run() {
        createGui();
    }

    private JButton createButton(String text, int mnemonic, String tooltip) {
        JButton button = new JButton(text);
        button.setMnemonic(mnemonic);
        button.setToolTipText(tooltip);
        button.addActionListener(this);
        return button;
    }

    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(createButton(START, KeyEvent.VK_S, "Start your engines!"));
        return buttonsPanel;
    }

    private void createGui() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(createMainPanel(), BorderLayout.CENTER);
        frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createMainPanel() {
        JPanel mainPanel = new JPanel(new GridLayout(0, 4, 10, 10));
        for (PanelFoo pf : fooPanels) {
            mainPanel.add(pf);
        }
        return mainPanel;
    }

    private void start() {
        for (PanelFoo pf : fooPanels) {
            pf.start();
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new FormMain());
    }
}

class PanelFoo extends JPanel implements PropertyChangeListener {
    private static final long serialVersionUID = 4029040375048696554L;

    private JProgressBar  progressBar;
    private JTextArea  textArea;

    public PanelFoo() {
        super(new BorderLayout());
        textArea = new JTextArea(20, 10);
        JScrollPane scrollPane = new JScrollPane(textArea);
        add(scrollPane, BorderLayout.CENTER);
        progressBar = new JProgressBar(0, 100);
        add(progressBar, BorderLayout.PAGE_END);
    }

    public void appendText(String text) {
        textArea.append(text);
        textArea.append("\n");
    }

    @Override // java.beans.PropertyChangeListener
    public void propertyChange(PropertyChangeEvent evt) {
        if ("progress".equals(evt.getPropertyName())) {
            int progress = (Integer) evt.getNewValue();
            progressBar.setValue(progress);            
        }
    }

    public void start() {
        WorkerDoSomething worker = new WorkerDoSomething(this);
        worker.addPropertyChangeListener(this);
        worker.execute();
    }
}

class WorkerDoSomething extends SwingWorker<Void, Long> {
    private PanelFoo  fooPanel;
    private Random r = new Random();

    public WorkerDoSomething(PanelFoo pf) {
        fooPanel = pf;
    }

    @Override
    protected Void doInBackground() throws Exception {
        Integer randomNumber = randomInt(10000000, 1000000000);
        long j;
        int progress = 0;
        final int onePerCent = randomNumber / 100;
        final int onePerMillion = onePerCent / 10;
        for (j = 0; j <= randomNumber; j++) {
            if (j % onePerCent == 0) {
                try {
                    Thread.sleep(1000);
                }
                catch (InterruptedException xInterrupted) {
                    // Ignore
                }
                progress = (int) j / onePerCent;
                setProgress(progress);
            }
            if (j % onePerMillion == 0) {
                publish(j);
            }
        }
        return null;
    }

    @Override
    protected void process(List<Long> numbers) {
        Long number = numbers.get(numbers.size() - 1);
        fooPanel.appendText(String.valueOf(number));
    }

    private Integer randomInt(final int min, final int max) {
        final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;
        return randomNumber;
    }
}

从技术上讲,以上内容回答了您的问题,即:

Is there a solution for this?

我相信我已经提供了一个解决方案,即使我无法解释它为什么有效。