Справочная информация:
Хорошо известное требование Swing о наилучшей практике состоит в том, что код, взаимодействующий со структурой Swing, также должен выполняться в EDT (Thread Dispatch Thread).
Таким образом, я изменил свой код, чтобы мои обновления на основе JFreeChart работали в EDT. Тем не менее, полная задача отображения диаграммы, которая обычно занимает около 7 минут до окончания sh в «нормальном» потоке, становится задачей нескольких часов при работе в EDT!
Что я делать неправильно? Я неправильно понял урок Swing Concurrency? Мне действительно нужно запустить org.jfree.data.time.TimeSeries.addOrUpdate(date, double)
внутри EDT?
Пожалуйста, сообщите!
Подробности:
Нажав кнопку Swing GUI, моя программа запускает трудоемкую задачу. По сути, он читает (большой) файл с парными значениями (date, double), а затем показывает их, используя инфраструктуру JFreeChart.
Поскольку это трудоемкая задача при чтении и отображении данных, JProgreessBar
показывает пользователю статус прогресса на переднем плане, а график обновляется в фоновом режиме (пользователь по-прежнему может визуально видеть каждое обновление графика за индикатором выполнения).
Это работало нормально, пока я не решил просмотрите код, чтобы мои данные диаграммы обновлялись и отображались в Swing EDT. Затем завершенное задание, которое обычно занимало около 7 минут до окончания sh, начинало занимать несколько часов до завершения!
Вот список потоков, которые я использую:
1) Поскольку задача запускается прослушивателем Swing Button, она выполняется в режиме EDT. JProgressBar
также работает в этом же потоке;
2) При отображении JProgressBar
второй («нормальный») поток создается и выполняется в фоновом режиме. Вот где проделана тяжелая работа.
Включает обновление статуса JProgressBar
в другом потоке (путем вызова JProgressBar.setvalue()
) и обновление моего JFreeChart
графика (путем вызова * 1049). *, который автоматически обновляет org.jfree.chart.ChartPanel
).
Обновление графика во втором («нормальном») потоке обычно занимало около 7 минут до окончания sh. Без каких-либо заметных проблем.
Однако, зная, что большинство методов объекта Swing не являются "потоко-безопасными", а ChartPanel
является просто компонентом Swing GUI для отображения объекта JFreeChart
, я решил чтобы запустить код обновления моего графика TimeSeries.addOrUpdate(date, double)
внутри EDT.
Все еще работающий во втором «нормальном» потоке, я протестировал следующий асинхронный код:
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 минут до конца sh, теперь начинало занимать несколько часов!
Итак, мой вопрос:
Что я делаю не так? Я неправильно понял урок Swing Concurrency? Действительно ли мне нужно запускать TimeSeries.addOrUpdate (date, double) внутри EDT?
Пожалуйста, сообщите!
UPDATE:
Полный код был бы слишком велик для показа здесь, но вы можете найти снимок кода ниже.
Возможно, единственное, что заметно в коде, это то, что я использую Reflection. Это потому, что я использую обобщенный класс c 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();
}
}
}
}