| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Java: GUI и Java FX приложения > JTable, AbstractTableModel и множество потоков |
| Автор: User1302 18.11.2010, 01:53 | ||
| Здравствуйте, уважаемые форумчане! Я новичок в Java. У меня возникла проблеммка с обновлением данных в JTable, кот. имеет моделью AbstractTableModel с List<> в качестве источника записей. Раньше не программировал интерфейс и возникшая ситуация ставит под сомнение правильность всей моей модели приложения. Попробую объяснить: 1. Есть обьекты, названные мною "Section", имеют свои поля (Код, Дата, Комментарий и проч.) Эти обьекты я хочу видеть в таблице JTable в отдельной строчке для каждого обьекта, где в столбцах - значения полей обьекта Section. Обьекты Section хранятся в массиве типа List<Section>. Массив List<Section> может пополняться новыми обьектами Section, также обьекты могут быть удалены из массива. 2. Таблица JTable использует AbstractTableModel, кот. берет данные из массива обьектов List<Section>. Вот примерный код, как это происходит:
3. События. Действия, кот. я хочу выполнять над обьектом Section: a) изменять его поля; Действия, кот. я хочу выполнять над массивом List<Section>: a) добавлять обьекты Section в массив; b) удалять обьекты Section из массива; Требуется, чтоб об этом узнавала таблица (модель) и при каждом изменении обьекта/списка обьектов обновлялись данные таблицы. Для простоты и по неопытности сейчас я это сделал так: Модель является подписчиком событий изменения обьекта Section - метод sectionUpdated(Section section){} и подписчиком событий изменения массива List<Section> - методы sectionAdded(Section section) и sectionWillBecomeRemoved(Section section). Соответственно в указанных методах вызываются методы модели AbstractTableModel - fireTableRowsUpdated(r, r);fireTableRowsInserted(r, r);fireTableRowsDeleted(r, r); А теперь собственно проблема: все работает, пока обновления происходят в одном потоке. Поскольку действия, производимые над обьектом Section занимают некот. время и над разными экземплярами Section могут одновременно выполняться действия, каждое действие выполняется в отдельном потоке. Когда действие завершается, обьект меняет свое состояние и об этом узнают все подписчики обьекта Section ч-з метод sectionUpdated(this), в их числе оказывается и моя модель таблицы. В отдельном потоке также могут производиться действия по добавлению и удалению в List<Section> обьектов. И вот когда происходит удаление подряд сразу нескольких обьектов Section - уведомляется об этом модель таблицы, выполняется fireTableRowsDeleted(r, r); и вылетает исключение: Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 1 Причем не всегда и при малом количестве обьектов (до десяти) все в порядке. Больше - обязательно исключение. Хотя пару раз не вылетало и при сотне обьектов. Блокировки synchronized не помогают. Думаю, проблема вот. в чем - из модели удаляются обьекты(строки) в том потоке, кот. это вызвал, а вот слушатели модели, в том числе JTable обновляются в другом потоке (EventDispatchThread???) и пока это происходит другой поток изменяет модель и уменьшает количество обьектов в массиве List<Section> и получается выход за пределы массива. Если я неправ, прошу меня поправить! В общем, прошу помощи в виде более-менее конкретных решений, типа "посмотри в сторону EventQueue.InvokeLater", или "SwingWorker", "EventDispatchThread". Перечисленные вещи крутятся в голове, но вплотную работать с ними не приходилось пока. Самое простое решение, кот. пришло в голову - запускать в самой модели отдельный поток на изменение модели, а удаляемые обьекты ставить в очередь. Но сейчас мне кажется это глупым. Буду очень признателен за все возможные варианты решений, в том числе радикальных, требующих пускай даже изменения всей модели приложения, ведь истина дороже! Повторяю, написать готовое решение я не прошу, достаточно совета, но за код тоже буду благодарен! На всякий случай прикрепляю рабочий вариант приложения, кот. сваял - очень сильно упрощенный вариант для удобства, но ошибка в нем присутствует. компилируется просто: javac Main.java, запускаетя тоже: java Main Всех заранее благодарю! |
| Автор: COVD 18.11.2010, 05:01 | ||
Да. Любые изменения модели следует выполнять в потоке EventDispatchThread. Тогда изменение и перерисовка изменения будут выполняться в одном потоке, т.е. последовательно, а не одновременно. |
| Автор: User1302 18.11.2010, 10:38 | ||||
Попробовал сделать вот так:
В итоге вроде все хорошо работает. Правильно так делать? |
| Автор: Skipy 18.11.2010, 11:31 |
| А sectionList у Вас - это модель таблицы? Не получится, что после получения события таблица обратится к модели для перерисовки, а Вы еще не удалили строку? Я бы поставил sectionList.remove(section) перед уведомлением. |
| Автор: User1302 18.11.2010, 12:30 | ||
Думаю, вы правы - исправил, хотя работает и так, и так. sectionList - это список в модели, откуда строчки дергаются. ... Разобрался немного с моделью событий Swing. Запускать изменения интерфейса через "SwingUtilities.invokeLater()" мне кажется правильным решением. Если это не единственное решение, прошу показать еще варианты, если они есть. А в общем, думаю, тему можно считать закрытой. Спасибо COVD, Skipy, плюсики ставить еще не получается |
| Автор: jk1 18.11.2010, 13:54 | ||
Модератор: Поставил |