【问题标题】:Waiting for a task inside a thread pool using SwingWorker java使用 SwingWorker java 在线程池中等待任务
【发布时间】:2021-01-06 05:44:32
【问题描述】:

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

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

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

【问题讨论】:

  • 我认为你应该在调试器中查看这个,告诉我们哪些线程被阻塞,以及线程在什么对象或条件下被阻塞。我不清楚发生了什么。
  • 所有这些都在运行...刚刚添加了打印

标签: java multithreading swing user-interface swingworker


【解决方案1】:

由于 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();
      }
    });

【讨论】:

  • 是的……没错。其实什么都没有。如果你提到了,我只需要删除。尴尬。
【解决方案2】:

您是否阅读了SwingWorker 类的javadoc?摘录如下:

类 SwingWorker
类型参数:
T - 此 SwingWorker 的 doInBackground 和 get 方法返回的结果类型
V - 此 SwingWorker 的发布和处理方法用于执行中间结果的类型

您在doInBackground() 方法中正确调用了方法publish(),但您没有覆盖方法process()。请参阅Tasks that Have Interim Results。假设你的publish()方法真的应该是process()方法,那么这应该是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 如何 - 在 SwingWorker 中放置冗长的任务

这是我对您的应用程序的重写。我冒昧地重写了部分内容,但基本上你只需要注意WorkerDoSomething 类。

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;
    }
}

我想说的是,从技术上讲,以上回答了您的问题,即:

有解决办法吗?

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

【讨论】:

  • 感谢您的帮助!其实我真的需要他们做“无”(无效)。正如我所提到的,我将定期汇集一个数据库。所以...还是谢谢
  • @MarllonNasser 所以你说你不需要调用方法publish(),因此你不需要覆盖方法process()。对吗?
  • 是的!事实上,我需要一个自定义方法来更新/插入 textArea 中的文本。在最终版本中,我不会覆盖它们。
猜你喜欢
  • 2019-10-08
  • 2015-07-13
  • 1970-01-01
  • 2011-06-02
  • 2019-09-18
  • 2018-07-24
  • 1970-01-01
  • 1970-01-01
  • 2015-08-06
相关资源
最近更新 更多