外部 ProgressBar 不是 运行 与 Button Action Listener 并发
External ProgressBar is not running in concurrent with Button Action Listener
我用 Swing worker 创建了一个外部 class,运行 是进度条。这是代码,
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);
}
}
}
当我在 main class 中调用上面的代码时,上面的代码有效,如下所示。
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
ExtProgress t = new ExtProgress();
t.runCalc();
}
});
}
但是,当我尝试在从数据库中获取大量行的操作按钮中调用相同内容时,大约需要 15-20 分钟。 progressbar 启动,一旦 db 进程启动,progressbar 被冻结,而 db statistics fetched.once 漫长的过程结束,progress bar 继续 运行 再次。
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();
}
});
}
}
当我点击 GO 按钮时,进度条开始填充。
代表您的应用程序的 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, 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();
}
}
}
}
此代码的问题在于 CheckStorage.DBVaultCheck()
在 EDT 上启动了一个较长的进程。
Swing 是一个单线程库。所有绘画任务都在事件调度线程 (EDT) 中执行。
运行 EDT 上的长进程(如睡眠)使该线程保持忙碌,因此它不会做其他事情
比如更新gui。 gui 变得没有响应(冻结)。
假设 CheckStorage.DBVaultCheck()
不 更新 gui,您所要做的就是 运行 通过更改代码中的一行在不同线程上的长过程:
private void runLongProcess() {
new ExtProgressMonitor().runCalc();
new Thread(()->CheckStorage.DBVaultCheck()).start();
}
万一 CheckStorage.DBVaultCheck()
确实更新了图形用户界面,您必须采取措施确保这些更新发生在 EDT 上。 Swing gui 更新只能由 EDT 完成。
我用 Swing worker 创建了一个外部 class,运行 是进度条。这是代码,
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);
}
}
}
当我在 main class 中调用上面的代码时,上面的代码有效,如下所示。
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
ExtProgress t = new ExtProgress();
t.runCalc();
}
});
}
但是,当我尝试在从数据库中获取大量行的操作按钮中调用相同内容时,大约需要 15-20 分钟。 progressbar 启动,一旦 db 进程启动,progressbar 被冻结,而 db statistics fetched.once 漫长的过程结束,progress bar 继续 运行 再次。
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();
}
});
}
}
当我点击 GO 按钮时,进度条开始填充。
代表您的应用程序的 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, 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();
}
}
}
}
此代码的问题在于 CheckStorage.DBVaultCheck()
在 EDT 上启动了一个较长的进程。
Swing 是一个单线程库。所有绘画任务都在事件调度线程 (EDT) 中执行。
运行 EDT 上的长进程(如睡眠)使该线程保持忙碌,因此它不会做其他事情
比如更新gui。 gui 变得没有响应(冻结)。
假设 CheckStorage.DBVaultCheck()
不 更新 gui,您所要做的就是 运行 通过更改代码中的一行在不同线程上的长过程:
private void runLongProcess() {
new ExtProgressMonitor().runCalc();
new Thread(()->CheckStorage.DBVaultCheck()).start();
}
万一 CheckStorage.DBVaultCheck()
确实更新了图形用户界面,您必须采取措施确保这些更新发生在 EDT 上。 Swing gui 更新只能由 EDT 完成。