外部ProgressBar没有与Button Action Listener并发运行

问题描述

我用运行进度条的Swing worker创建了一个外部类。这是代码

public class ExtProgressMonitor extends JFrame {

private static final long serialVersionUID = 1L;
private static final String s = "Database Statistics Report is exectuing in the Background";
private JProgressBar progressBar = new JProgressBar(0,100);
private JLabel label = new JLabel(s,JLabel.CENTER);

public ExtProgressMonitor() {
    this.setLayout(new GridLayout(0,1));
    this.setTitle("Database Utility Execution");
    this.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
    this.add(progressBar);
    this.add(label);
    this.setSize(100,100);
    this.setLocationRelativeto(null);
    this.setVisible(true);
    pack();
}

public void runcalc() {
    progressBar.setIndeterminate(false);
    progressBar.setStringPainted(false);
    TwoWorker task = new TwoWorker();
    task.addPropertychangelistener(new Propertychangelistener() {

        @Override
        public void propertyChange(PropertyChangeEvent e) {
            if ("progress".equals(e.getPropertyName())) {
                progressBar.setIndeterminate(false);
                progressBar.setValue((Integer) e.getNewValue());
            }
        }
    });
    task.execute();
}

private class TwoWorker extends SwingWorker<Integer,Integer> {

    private static final int N = 500;
    private final DecimalFormat df = new DecimalFormat(s);
    Integer x = 1;

    @Override
    protected Integer doInBackground() throws Exception {
        if (!javax.swing.SwingUtilities.isEventdispatchThread()) {
            System.out.println("javax.swing.SwingUtilities.isEventdispatchThread() + returned false.");
        }
        for (int i = 1; i <= N; i++) {
            x = x - (((x * x - 2) / (2 * x)));
            setProgress(i * (100 / N));
            publish(Integer.valueOf(x));
            Thread.sleep(1000); // simulate latency
        }
        return Integer.valueOf(x);
    }

    @Override
    protected void process(List<Integer> chunks) {
        for (Integer percent : chunks ) {
           progressBar.setValue(progressBar.getValue() + percent);
        }
    }
}

上面的代码在我像下面这样在主类中调用时起作用。

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

        @Override
        public void run() {
            ExtProgress t = new ExtProgress();
            t.runcalc();
        }
    });
}

但是,当我尝试在“操作”按钮中调用相同的操作以从数据库获取很多行时,大约需要15-20分钟。进度条启动后,一旦db进程启动,进度条将被冻结,同时获取db统计信息。一旦长时间的处理结束,进度条将继续运行。

private void jButton1ActionPerformed(java.awt.event.ActionEvent e) throws Exception {                                         
         
    ExtProgressMonitor t = new ExtProgressMonitor();
    t.runcalc();
    CheckStorage.DBVaultCheck(Host,port,instance,workbook,Schema_Password,schema);
    //.. Rest of the process,check storage comes from another class. 
    });

您能帮我解决此问题吗?

解决方法

我刚刚在问题中发布的代码中添加了三行(并注释了另外两行)。我添加的行在下面的代码中指出,并在其前面加上注释。

import java.awt.EventQueue;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class ExtProgressMonitor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final String s = "Database Statistics Report is exectuing in the Background";
    private JProgressBar progressBar = new JProgressBar(0,100);
    private JLabel label = new JLabel(s,JLabel.CENTER);

    public ExtProgressMonitor() {
        this.setLayout(new GridLayout(0,1,10,10));
        this.setTitle("Database Utility Execution");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(progressBar);
        this.add(label);

        /*Added next three lines.*/
        JButton b = new JButton("GO");
        b.addActionListener(e -> runCalc());
        add(b);

        pack();
//        this.setSize(100,100);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
    }

    public void runCalc() {
        progressBar.setIndeterminate(false);
        progressBar.setStringPainted(false);
        TwoWorker task = new TwoWorker();
        task.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent e) {
                if ("progress".equals(e.getPropertyName())) {
                    progressBar.setIndeterminate(false);
                    progressBar.setValue((Integer) e.getNewValue());
                }
            }
        });
        task.execute();
    }

    private class TwoWorker extends SwingWorker<Integer,Integer> {
        private static final int N = 500;
        private final DecimalFormat df = new DecimalFormat(s);
        Integer x = 1;

        @Override
        protected Integer doInBackground() throws Exception {
            if (!javax.swing.SwingUtilities.isEventDispatchThread()) {
                System.out.println(
                        "javax.swing.SwingUtilities.isEventDispatchThread() + returned false.");
            }
            for (int i = 1; i <= N; i++) {
                x = x - (((x * x - 2) / (2 * x)));
                setProgress(i * (100 / N));
                publish(Integer.valueOf(x));
                Thread.sleep(1000); // simulate latency
            }
            return Integer.valueOf(x);
        }

        @Override
        protected void process(List<Integer> chunks) {
            for (Integer percent : chunks) {
                progressBar.setValue(progressBar.getValue() + percent);
            }
        }
    }
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ExtProgressMonitor t = new ExtProgressMonitor();
//                t.runCalc();
            }
        });
    }
}

当我单击“执行”按钮时,进度条开始填充。

,

代表您的应用程序的MRE可能如下所示:

import java.awt.GridLayout;
import java.text.DecimalFormat;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

public class SwingMain {

    SwingMain() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.add(new TestPanel());
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new SwingMain();
    }
}

class TestPanel extends JPanel {

    public TestPanel() {

        JButton btn = new JButton("Run long process");
        btn.addActionListener(evt -> runLongProcess());
        add(btn);
    }

    private void runLongProcess() {
        new ExtProgressMonitor().runCalc();
        CheckStorage.DBVaultCheck();
    }
}

//For a second frame it is recommended to use JDialog instead of JFRame
class ExtProgressMonitor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final String s = "Database Statistics Report is exectuing in the Background";
    private final JProgressBar progressBar = new JProgressBar(0,100);
    private final JLabel label = new JLabel(s,1));
        this.setTitle("Database Utility Execution");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(progressBar);
        this.add(label);
        this.setSize(100,100);
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        pack();
    }

    public void runCalc() {
        progressBar.setIndeterminate(false);
        progressBar.setStringPainted(false);
        new TwoWorker().execute();
    }

    private class TwoWorker extends SwingWorker<Integer,Integer> {

        private static final int N = 500;
        private final DecimalFormat df = new DecimalFormat(s);
        Integer x = 1;

        @Override
        protected Integer doInBackground() throws Exception {
            for (int i = 1; i <= N; i++) {
                x = x - (x * x - 2) / (2 * x);
                publish(Integer.valueOf(x));
                Thread.sleep(1000); // simulate latency
            }
            return Integer.valueOf(x);
        }

        @Override
        protected void process(List<Integer> chunks) {
            for (Integer percent : chunks ) {
                progressBar.setValue(progressBar.getValue() + percent);
            }
        }
    }
}

class CheckStorage{

    private static final int  LIMIT = 1000;
    public static void DBVaultCheck() {
        int counter = 0;
        while(counter ++ < LIMIT)   { //simulate long process
            try {
                Thread.sleep(1000);
                System.out.println(counter);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}

此代码的问题是CheckStorage.DBVaultCheck()EDT上开始了一个漫长的过程。
Swing是单个线程库。所有绘画任务都在事件分派器线程(EDT)中执行。 在EDT上运行长进程(例如睡眠)会使该线程保持繁忙,因此它不会做其他事情 喜欢更新gui。 gui变得无响应(冻结)。
假设CheckStorage.DBVaultCheck() 更新gui,您所要做的就是通过更改代码中的一行来在另一个线程上运行长进程:

    private void runLongProcess() {
        new ExtProgressMonitor().runCalc();
        new Thread(()->CheckStorage.DBVaultCheck()).start();
    }

如果CheckStorage.DBVaultCheck()确实更新了gui,则必须采取措施以确保这些更新发生在EDT 。 Swing gui更新只能由EDT完成。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...