外部进度条与按钮动作监听器不并发运行

huangapple go评论80阅读模式
英文:

External ProgressBar is not running in concurrent with Button Action Listener

问题

我创建了一个带有 Swing Worker 的外部类用于运行进度条以下是代码

public class ExtProgressMonitor extends JFrame {

    private static final long serialVersionUID = 1L;
    private static final String s = "数据库统计报表正在后台执行";
    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("数据库实用程序执行");
        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() + 返回 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); // 模拟延迟
            }
            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();
        }
    });
}

然而,当我尝试在获取大量数据库行的操作按钮中调用相同的代码时,需要大约15-20分钟。进度条被启动,一旦数据库进程开始,进度条就会冻结,而数据库统计数据被获取。一旦长时间的进程结束,进度条会继续运行。

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);
    // ... 其余的过程,check storage来自另一个类。
}

请问您能帮助我解决这个问题吗?

英文:

I created one external class with Swing worker that runs the progress bar. This is the code,

public class ExtProgressMonitor extends JFrame {
private static final long serialVersionUID = 1L;
private static final String s = &quot;Database Statistics Report is exectuing in the Background&quot;;
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(&quot;Database Utility Execution&quot;);
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 (&quot;progress&quot;.equals(e.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer) e.getNewValue());
}
}
});
task.execute();
}
private class TwoWorker extends SwingWorker&lt;Integer, Integer&gt; {
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(&quot;javax.swing.SwingUtilities.isEventDispatchThread() + returned false.&quot;);
}
for (int i = 1; i &lt;= 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&lt;Integer&gt; chunks) {
for (Integer percent : chunks ) {
progressBar.setValue(progressBar.getValue() + percent);
}
}
}

The above code works when I call it in main class like below .

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
ExtProgress t = new ExtProgress();
t.runCalc();
}
});
}

However When I try to call the same in my Action button that fetches a lot of row from database, takes around 15-20 minutes. progressbar gets launched, once the db process starts, the progressbar is frozen, while the db statistics is fetched.once the long process is over, the progress bar continues to run again.

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

Can you please help me fix this issue?

答案1

得分: 0

我刚刚在你在问题中发布的代码中添加了三行代码(并注释掉了其他两行)。我添加的这些行在下面的代码中用注释标示出来。

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

        /*以下是添加的三行代码。*/
        JButton b = new JButton("GO");  // 翻译注释:创建一个名为“GO”的按钮
        b.addActionListener(e -> runCalc());  // 翻译注释:为按钮添加点击事件监听,调用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); // 模拟延迟
            }
            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();
            }
        });
    }
}

当我点击“GO”按钮时,进度条开始填充。

英文:

I just added three lines (and commented out two others) to the code you posted in your question. The lines I added are indicated in the below code with a comment preceding them.

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 = &quot;Database Statistics Report is exectuing in the Background&quot;;
    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(&quot;Database Utility Execution&quot;);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.add(progressBar);
        this.add(label);

        /*Added next three lines.*/
        JButton b = new JButton(&quot;GO&quot;);
        b.addActionListener(e -&gt; 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 (&quot;progress&quot;.equals(e.getPropertyName())) {
                    progressBar.setIndeterminate(false);
                    progressBar.setValue((Integer) e.getNewValue());
                }
            }
        });
        task.execute();
    }

    private class TwoWorker extends SwingWorker&lt;Integer, Integer&gt; {
        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(
                        &quot;javax.swing.SwingUtilities.isEventDispatchThread() + returned false.&quot;);
            }
            for (int i = 1; i &lt;= 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&lt;Integer&gt; 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();
            }
        });
    }
}

When I click on the GO button, the progress bar starts to fill.

答案2

得分: 0

An MRE representing your application could look like the following: 

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();
        new Thread(() -> CheckStorage.DBVaultCheck()).start();
    }
}

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

The problem with this code is that CheckStorage.DBVaultCheck() starts a long process on the EDT.
Swing is a single Thread library. All painting tasks are executed in the Event Dispatcher Thread (EDT).
Running long processes (such as sleep) on the EDT keeps this thread busy, so it does not do other things
like updating the GUI. The GUI becomes unresponsive (freezes).
Assuming CheckStorage.DBVaultCheck() does not update the GUI, all you have to do is run the long process on a different thread by changing a single line in the code:

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

In case CheckStorage.DBVaultCheck() does update the GUI, you'll have to take measures to make sure that those updates occur on the EDT. Swing GUI updates should only be done by the EDT.

英文:

An MRE representing your application could look like the following:

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(&quot;Run long process&quot;);
btn.addActionListener(evt -&gt; 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 = &quot;Database Statistics Report is exectuing in the Background&quot;;
private final JProgressBar progressBar = new JProgressBar(0, 100);
private final JLabel label = new JLabel(s, JLabel.CENTER);
public ExtProgressMonitor() {
this.setLayout(new GridLayout(0, 1));
this.setTitle(&quot;Database Utility Execution&quot;);
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&lt;Integer, Integer&gt; {
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 &lt;= 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&lt;Integer&gt; 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 ++ &lt; LIMIT)	{ //simulate long process
try {
Thread.sleep(1000);
System.out.println(counter);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}

The problem with this code is that CheckStorage.DBVaultCheck() starts a long process on the EDT. <br/>
Swing is a single Thread library. All painting tasks are executed in the Event Dispatcher Thread (EDT).
Running long processes (such as sleep) on the EDT makes keeps this thread busy, so it does not do other things
like updating the gui. The gui becomes unresponsive (freezes). <br/>
Assuming CheckStorage.DBVaultCheck() does not update the gui, all you have to do is run the long process on a different thread by changing a single line in the code:

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

In case CheckStorage.DBVaultCheck() does update the gui, you'l have to take measures to make sure that those updates occur on the EDT. Swing gui updates should only be done by the EDT.

huangapple
  • 本文由 发表于 2020年8月28日 21:07:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/63634390.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定