更快地通知 PropertyChangeListener
Notify PropertyChangeListener faster
所以我正在创建一个 JProgressBar 来显示 CSV 操作的进度,其中读取每一行并检查是否有强制性的空值 (NOT NULL)列。为此,我创建了一个 SwingWorker 任务,用于处理将文件中的行数转换为最大进度值的 100%,并以正确的速率累加进度。
那是 SwingWorker:
public static class Task extends SwingWorker<String, Object> {
private int counter;
private double rate;
public Task(int max) {
// Adds the PropertyChangeListener to the ProgressBar
addPropertyChangeListener(
ViewHandler.getExportDialog().getProgressBar());
rate = (float)100/max;
setProgress(0);
counter = 0;
}
/** Increments the progress in 1 times the rate based on maximum */
public void step() {
counter++;
setProgress((int)Math.round(counter*rate));
}
@Override
public String doInBackground() throws IOException {
return null;
}
@Override
public void done() {
Toolkit.getDefaultToolkit().beep();
System.out.println("Progress done.");
}
}
我的PropertyChangeListener
,这是由JProgressBar wrapper实现的:
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer) evt.getNewValue());
}
}
然后,在我实际使用它的地方,我用我需要的处理覆盖 doInBackground()
方法,在每次迭代时调用 step()
。
Task read = new Task(lines) {
@Override
public String doInBackground() throws IOException {
while(content.hasNextValue()) {
step();
// Processing
}
return output.toString();
}
};
read.execute();
return read.get();
那么发生了什么:处理工作并成功,然后调用 done()
,紧接着 propertyChange()
注册两个 'state' 事件和一个 'progress' 事件,将 ProgressBar 的进度从 0% 设置为 100%。
正在发生的事情 我认为正在发生的事情( 用于澄清)在 JavaDocs 中有描述:
因为在事件调度线程上异步通知 PropertyChangeListeners,所以在调用任何 PropertyChangeListeners 之前可能会多次调用 setProgress 方法。出于性能目的,所有这些调用都合并为一个仅包含最后一个调用参数的调用。
所以,毕竟,我的问题是:我做错了什么吗?如果没有,有没有办法让事件调度线程在 onProgress() 发生时或至少不时通知 PropertyChangeListeners?
观察:我正在测试的处理过程需要 3~5 秒。
您的问题在这里:
read.execute();
return read.get();
get()
是一个阻塞调用,因此在执行您的 worker 后立即从事件线程调用它会阻塞事件线程 和您的 GUI。
相反,它应该从回调方法(例如 done()
方法或从 属性 更改侦听器在工作人员将其状态 属性 更改为 SwingWorker.StateValue.DONE
.
例如
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
@SuppressWarnings("serial")
public class TestSwingWorkerGui extends JPanel {
private JProgressBar progressBar = new JProgressBar(0, 100);
private Action myAction = new MyAction("Do It!");
public TestSwingWorkerGui() {
progressBar.setStringPainted(true);
add(progressBar);
add(new JButton(myAction));
}
private class MyAction extends AbstractAction {
public MyAction(String name) {
super(name);
}
@Override
public void actionPerformed(ActionEvent e) {
myAction.setEnabled(false);
Task read = new Task(30) {
@Override
public String doInBackground() throws Exception {
int counter = getCounter();
int max = getMax();
while (counter < max) {
counter = getCounter();
step();
TimeUnit.MILLISECONDS.sleep(200);
}
return "Worker is Done";
}
};
read.addPropertyChangeListener(new MyPropListener());
read.execute();
}
}
private class MyPropListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if ("progress".equals(name)) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer) evt.getNewValue());
} else if ("state".equals(name)) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
myAction.setEnabled(true);
@SuppressWarnings("unchecked")
SwingWorker<String, Void> worker = (SwingWorker<String, Void>) evt.getSource();
try {
String text = worker.get();
System.out.println("worker returns: " + text);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
private static void createAndShowGui() {
TestSwingWorkerGui mainPanel = new TestSwingWorkerGui();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Task extends SwingWorker<String, Void> {
private int counter;
// private double rate;
private int max;
public Task(int max) {
// Adds the PropertyChangeListener to the ProgressBar
// addPropertyChangeListener(gui);
// !!rate = (float)100/max;
this.max = max;
setProgress(0);
counter = 0;
}
/** Increments the progress in 1 times the rate based on maximum */
public void step() {
counter++;
int progress = (100 * counter) / max;
progress = Math.min(100, progress);
setProgress(progress);
// setProgress((int)Math.round(counter*rate));
}
public int getCounter() {
return counter;
}
public int getMax() {
return max;
}
@Override
public String doInBackground() throws Exception {
return null;
}
@Override
public void done() {
Toolkit.getDefaultToolkit().beep();
System.out.println("Progress done.");
}
}
所以我正在创建一个 JProgressBar 来显示 CSV 操作的进度,其中读取每一行并检查是否有强制性的空值 (NOT NULL)列。为此,我创建了一个 SwingWorker 任务,用于处理将文件中的行数转换为最大进度值的 100%,并以正确的速率累加进度。
那是 SwingWorker:
public static class Task extends SwingWorker<String, Object> {
private int counter;
private double rate;
public Task(int max) {
// Adds the PropertyChangeListener to the ProgressBar
addPropertyChangeListener(
ViewHandler.getExportDialog().getProgressBar());
rate = (float)100/max;
setProgress(0);
counter = 0;
}
/** Increments the progress in 1 times the rate based on maximum */
public void step() {
counter++;
setProgress((int)Math.round(counter*rate));
}
@Override
public String doInBackground() throws IOException {
return null;
}
@Override
public void done() {
Toolkit.getDefaultToolkit().beep();
System.out.println("Progress done.");
}
}
我的PropertyChangeListener
,这是由JProgressBar wrapper实现的:
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer) evt.getNewValue());
}
}
然后,在我实际使用它的地方,我用我需要的处理覆盖 doInBackground()
方法,在每次迭代时调用 step()
。
Task read = new Task(lines) {
@Override
public String doInBackground() throws IOException {
while(content.hasNextValue()) {
step();
// Processing
}
return output.toString();
}
};
read.execute();
return read.get();
那么发生了什么:处理工作并成功,然后调用 done()
,紧接着 propertyChange()
注册两个 'state' 事件和一个 'progress' 事件,将 ProgressBar 的进度从 0% 设置为 100%。
正在发生的事情 我认为正在发生的事情(
因为在事件调度线程上异步通知 PropertyChangeListeners,所以在调用任何 PropertyChangeListeners 之前可能会多次调用 setProgress 方法。出于性能目的,所有这些调用都合并为一个仅包含最后一个调用参数的调用。
所以,毕竟,我的问题是:我做错了什么吗?如果没有,有没有办法让事件调度线程在 onProgress() 发生时或至少不时通知 PropertyChangeListeners?
观察:我正在测试的处理过程需要 3~5 秒。
您的问题在这里:
read.execute();
return read.get();
get()
是一个阻塞调用,因此在执行您的 worker 后立即从事件线程调用它会阻塞事件线程 和您的 GUI。
相反,它应该从回调方法(例如 done()
方法或从 属性 更改侦听器在工作人员将其状态 属性 更改为 SwingWorker.StateValue.DONE
.
例如
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
@SuppressWarnings("serial")
public class TestSwingWorkerGui extends JPanel {
private JProgressBar progressBar = new JProgressBar(0, 100);
private Action myAction = new MyAction("Do It!");
public TestSwingWorkerGui() {
progressBar.setStringPainted(true);
add(progressBar);
add(new JButton(myAction));
}
private class MyAction extends AbstractAction {
public MyAction(String name) {
super(name);
}
@Override
public void actionPerformed(ActionEvent e) {
myAction.setEnabled(false);
Task read = new Task(30) {
@Override
public String doInBackground() throws Exception {
int counter = getCounter();
int max = getMax();
while (counter < max) {
counter = getCounter();
step();
TimeUnit.MILLISECONDS.sleep(200);
}
return "Worker is Done";
}
};
read.addPropertyChangeListener(new MyPropListener());
read.execute();
}
}
private class MyPropListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if ("progress".equals(name)) {
progressBar.setIndeterminate(false);
progressBar.setValue((Integer) evt.getNewValue());
} else if ("state".equals(name)) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
myAction.setEnabled(true);
@SuppressWarnings("unchecked")
SwingWorker<String, Void> worker = (SwingWorker<String, Void>) evt.getSource();
try {
String text = worker.get();
System.out.println("worker returns: " + text);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
private static void createAndShowGui() {
TestSwingWorkerGui mainPanel = new TestSwingWorkerGui();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Task extends SwingWorker<String, Void> {
private int counter;
// private double rate;
private int max;
public Task(int max) {
// Adds the PropertyChangeListener to the ProgressBar
// addPropertyChangeListener(gui);
// !!rate = (float)100/max;
this.max = max;
setProgress(0);
counter = 0;
}
/** Increments the progress in 1 times the rate based on maximum */
public void step() {
counter++;
int progress = (100 * counter) / max;
progress = Math.min(100, progress);
setProgress(progress);
// setProgress((int)Math.round(counter*rate));
}
public int getCounter() {
return counter;
}
public int getMax() {
return max;
}
@Override
public String doInBackground() throws Exception {
return null;
}
@Override
public void done() {
Toolkit.getDefaultToolkit().beep();
System.out.println("Progress done.");
}
}