SwingWorker线程同步

问题描述 投票:0回答:1

我创建了一个GUI应用,其中正在执行4个单独的SwingWorker线程。他们的进度由单独的进度条和旁边的数字指示(动态更新)。底部还有一个“总计”标签,该标签应该是所有4个线程的进度之和。但是,由于比赛条件,总计未正确计算。到目前为止,我已尝试使用syncrhonized关键字并利用SwingWorker的publish()process()方法。没事。还有一个“暂停”和“继续”按钮可以使用,但是在“ GrandTotal”数字中却产生了更大的差异。

这是我的对话框代码:

public class ThreadTestDialog extends JDialog {
    private JPanel contentPane;
    private JButton buttonStart;
    private JButton buttonPause;
    private JProgressBar progressBar1;
    private JProgressBar progressBar2;
    private JProgressBar progressBar3;
    private JProgressBar progressBar4;
    private JButton buttonResume;
    private JLabel labelThread1;
    private JLabel labelThread2;
    private JLabel labelThread3;
    private JLabel labelThread4;
    private JLabel labelThread1Total;
    private JLabel labelThread2Total;
    private JLabel labelThread3Total;
    private JLabel labelThread4Total;
    private JLabel labelGrandTotal;
    private JLabel labelGrandTotalValue;

    public AppThread thread1 = new AppThread(labelThread1Total, progressBar1, 30, labelGrandTotalValue);
    public AppThread thread2 = new AppThread(labelThread2Total, progressBar2, 75, labelGrandTotalValue);
    public AppThread thread3 = new AppThread(labelThread3Total, progressBar3, 50, labelGrandTotalValue);
    public AppThread thread4 = new AppThread(labelThread4Total, progressBar4, 20, labelGrandTotalValue);

    public ThreadTestDialog() {
        setContentPane(contentPane);
        setModal(true);
        getRootPane().setDefaultButton(buttonStart);

        buttonStart.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onStart();
            }
        });

        buttonPause.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onPause();
            }
        });

        buttonResume.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onResume();
            }
        });

        // call dispose() when cross is clicked
        setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                dispose();
            }
        });

        // call dispose() on ESCAPE
        contentPane.registerKeyboardAction(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                dispose();
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    }

    private void onStart() {
        thread1.execute();
        thread2.execute();
        thread3.execute();
        thread4.execute();
    }

    private void onPause() {
        thread1.setFlag(false);
        thread2.setFlag(false);
        thread3.setFlag(false);
        thread4.setFlag(false);
    }

    private void onResume() {
        int tempValue;

        tempValue = thread1.getValue();
        thread1 = new AppThread(tempValue, labelThread1Total, progressBar1, 30, labelGrandTotalValue);
        thread1.execute();

        tempValue = thread2.getValue();
        thread2 = new AppThread(tempValue, labelThread2Total, progressBar2, 75, labelGrandTotalValue);
        thread2.execute();

        tempValue = thread3.getValue();
        thread3 = new AppThread(tempValue, labelThread3Total, progressBar3, 50, labelGrandTotalValue);
        thread3.execute();

        tempValue = thread4.getValue();
        thread4 = new AppThread(tempValue, labelThread4Total, progressBar4, 20, labelGrandTotalValue);
        thread4.execute();

    }

    public static void main(String[] args) {
        ThreadTestDialog dialog = new ThreadTestDialog();
        dialog.pack();
        dialog.setVisible(true);
        System.exit(0);
    }
}

这是我的SwingWorker自定义类:

import javax.swing.*;
import java.util.List;

public class AppThread extends SwingWorker<Void, Integer> {
    private int value=0;
    private int sleepTime;
    private JLabel label;
    private JProgressBar progressBar;
    private JLabel grandTotal;
    private boolean flag=true;

    public AppThread (JLabel label, JProgressBar progressBar, int sleepTime, JLabel grandTotal) {
        this.sleepTime = sleepTime;
        this.label = label;
        this.progressBar = progressBar;
        this.grandTotal = grandTotal;
    }

    public AppThread (int value, JLabel label, JProgressBar progressBar, int sleepTime, JLabel grandTotal) {
        this.value = value;
        this.sleepTime = sleepTime;
        this.label = label;
        this.progressBar = progressBar;
        this.grandTotal = grandTotal;
    }

    public Void doInBackground() {
        for (int i = value; i <= 100; i++) {
            if (!flag) break;
            this.value = i;
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            label.setText(Integer.toString(value));
            progressBar.setValue(value);
            progressBar.setStringPainted(true);

            grandTotal.setText(Integer.toString(Integer.parseInt(grandTotal.getText()) + 1));
        }
        return null;
    }

    public void done() {
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getValue() {
        return value;
    }

    }
}

我将非常感谢您提出有关如何同步工作线程,以便它们正确更新“ GrandTotal”标签的建议。

java multithreading swing synchronization swingworker
1个回答
1
投票

您违反了Swing的线程规则,从事件分派线程的上下文外部更新了UI,SwingWorker可以帮助您。

[避免将您的UI对象的引用传递给SwingWorker,而是使用其process方法来更新某些状态模型,或者使用PropertyChangeListener支持来间接地更新UI。

下面是一个简单的示例,它使用PropertyChangeListener支持来更新进度条。

注意,我已经将AppThread与UI分离了,所以UI现在承担了更新UI的责任。该示例还可以动态扩展,因此您可以通过简单地更新创建它们的AppThread来增加for-loop的数量。

ThreadTestDialog
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JProgressBar;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ThreadTestDialog extends JDialog {

    private JButton buttonStart;
    private Map<AppThread, JProgressBar> progressBars = new HashMap<>(4);
    private JProgressBar pbGrandTotal;

    public ThreadTestDialog() {
        setModal(true);
        getRootPane().setDefaultButton(buttonStart);

        buttonStart = new JButton("Start");
        buttonStart.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                onStart();
            }
        });

        // call dispose() when cross is clicked
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridwidth = GridBagConstraints.REMAINDER;

        Random rnd = new Random();
        for (int index = 0; index < 4; index++) {
            AppThread appThread = new AppThread(rnd.nextInt(1000));
            appThread.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    AppThread worker = (AppThread)evt.getSource();
                    String name = evt.getPropertyName();
                    if (name.equals("progress")) {
                        JProgressBar pb = progressBars.get(worker);
                        pb.setValue(worker.getProgress());

                    } else if (name.equals("done")) {
                        // Now you can do something when the worker completes...
                    }
                    updateTotalProgress();
                }
            });
            JProgressBar pb = new JProgressBar(0, 100);

            progressBars.put(appThread, pb);
            add(pb, gbc);
        }

        pbGrandTotal = new JProgressBar(0, progressBars.size() * 100);
        add(pbGrandTotal, gbc);

        add(buttonStart, gbc);
    }

    protected void updateTotalProgress() {
        int totalProgress = 0;
        for (Map.Entry<AppThread, JProgressBar> entry : progressBars.entrySet()) {
            totalProgress += entry.getKey().getProgress();
        }
        pbGrandTotal.setValue(totalProgress);
    }

    private void onStart() {
        for (Map.Entry<AppThread, JProgressBar> entry : progressBars.entrySet()) {
            entry.getKey().execute();
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }
                ThreadTestDialog dialog = new ThreadTestDialog();
                dialog.pack();
                dialog.setVisible(true);
            }
        });
    }
}

AppThread

import javax.swing.SwingWorker;

public class AppThread extends SwingWorker<Void, Integer> {

    private int value = 0;
    private int sleepTime;

    public AppThread(int sleepTime) {
        this.sleepTime = sleepTime;
    }

    public Void doInBackground() {
        for (int i = value; i <= 100; i++) {
            this.value = i;
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setProgress(value);
        }
        return null;
    }

}

© www.soinside.com 2019 - 2024. All rights reserved.