背景:
一个著名的Swing最佳实践要求是与Swing框架进行交互也必须在EDT中执行(事件调度线程)。
因此,我更改了代码以使基于JFreeChart的更新可以在EDT中运行。但是,一个完整的图表显示任务通常需要大约7分钟才能在“正常”线程上完成,而在EDT中运行则要花费几个小时!
我做错了什么?我是否误解了Swing并发课程?我真的必须在EDT中运行org.jfree.data.time.TimeSeries.addOrUpdate(date, double)
吗?
请告知!
详细信息:
单击Swing GUI按钮,我的程序触发了一项耗时的任务。基本上,它读取具有对值(日期,双精度)的(大)文件,然后使用JFreeChart框架显示它们。
因为这是一项耗时的任务,所以在读取和显示数据时,JProgreessBar
在前台显示用户进度状态,而在后台更新图表(用户仍然可以直观地看到每个图表更新,位于后台进度栏)。
这很好,直到我决定查看代码以更新图表数据并将其显示在Swing EDT中。然后,通常需要大约7分钟才能完成的完整任务开始需要花费几个小时才能完成!
这是我正在使用的线程列表:
1)由于任务是由Swing Button侦听器触发的,因此它正在EDT中运行。 JProgressBar
也正在同一线程中运行;
2]当显示JProgressBar
时,在后台创建并执行了第二个(“正常”)线程。这是繁重的工作。
[它包括另一个线程上的JProgressBar
状态的更新(通过调用JProgressBar.setvalue()
)和我的JFreeChart
图表的更新(通过调用TimeSeries.addOrUpdate(date, double)
,后者自动更新org.jfree.chart.ChartPanel
)。
在我的第二个(“正常”)线程中更新图表通常需要大约7分钟才能完成。没有任何明显的问题。
但是,由于知道大多数Swing对象方法都不是“线程安全的”并且ChartPanel
只是用于显示JFreeChart
对象的Swing GUI组件,因此我决定在EDT中运行图表更新代码TimeSeries.addOrUpdate(date, double)
。
仍然在第二个“正常”线程中运行,我使用以下异步代码进行了测试:
javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { TimeSeries.addOrUpdate(date, double); } });
但是我意识到在更新图表之前,我的JProgressBar将达到100%。我想这是可以预期的,因为显示图表数据比获取和处理数据要慢得多。
然后我尝试了以下同步代码:
try { javax.swing.SwingUtilities.invokeAndWait(new Runnable() { public void run() { TimeSeries.addOrUpdate(date, double); } }); } catch (InvocationTargetException | InterruptedException e) { e.printStackTrace(); }
这是我发现性能问题的地方:现在,过去需要大约7分钟才能完成的完整任务,而要花几个小时才能完成!
所以,我的问题是:
我在做什么错?我是否误解了Swing并发课程?我真的必须在EDT中运行TimeSeries.addOrUpdate(date,double)吗?
请告知!
更新:完整的代码太大了,无法在此处显示,但是您可以在下面找到代码快照。也许,关于代码的唯一值得注意的事情是我使用了反射。这是因为我使用了一个通用的ProgressBar类,该类在后台调用我将其作为参数发送的任何类(尽管在下面的快照中并未清楚显示)。//BUTTON LISTENER (EDT)
public void actionPerformed(ActionEvent arg0) {
new Process_offline_data();
}
public Process_offline_data() {
//GET DATA
String[][] data = get_data_from_file();
//CREATE PROGRESS BAR
int maximum_progressBar = data.length;
JProgressBar jpb = init_jpb(maximum_progressBar);
//JDIALOG MODAL WINDOW
JDialog jdialog = create_jdialog_window(jpb);
Object class_to_invoke_obj = (Object) new Show_data_on_chart();
String main_method_str = "do_heavy_staff";
Runnable r = new Runnable() {
public void run() {
//REFLECTION
Method method = null;
try {
method = class_to_invoke_obj.getClass().getDeclaredMethod(main_method_str, JProgressBar.class, String[][].class);
} catch (NoSuchMethodException | SecurityException e1) {
e1.printStackTrace();
jdialog.dispose(); //UNBLOCKS MAIN THREAD
return;
}
try {
method.invoke(class_to_invoke_obj, jpb, data);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
jdialog.dispose(); //UNBLOCKS MAIN THREAD
return;
}
//----------------
jdialog.dispose(); //UNBLOCKS MAIN THREAD
}
};
new Thread(r).start();
//----------------
//THIS IS STILL EDT
jdialog.setVisible(true); //BLOCKS HERE UNTIL THE THREAD CALLS jdialog.dispose();
}
public class Show_data_on_chart {
public void do_heavy_staff(JProgressBar jpb, String[][] data) {
TimeSeries time_series = get_TimeSeries(); //JFreeChart datamodel
int len = data.length;
for (int i=0; i<len; i++) {
jpb.setValue(i+1);
Millisecond x_axys_millisecond = convert_str2date(data[i][0]);
Double y_axys_double = convert_str2double(data[i][1]);
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
//AUTOMATICALLY UPDATES org.jfree.chart.ChartPanel
time_series.addOrUpdate(x_axys_millisecond, y_axys_double);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
}
}
背景:一个众所周知的Swing最佳实践要求是,与Swing框架进行交互的代码也必须在EDT(事件调度线程)中执行。因此,我将代码更改为...
这是我解决更新图表的方法。