Что происходит при нарушении политики потоков Swing? - PullRequest
6 голосов
/ 16 сентября 2009

В последние несколько лет я в основном занимался разработкой пользовательского интерфейса в Eclipse, которая очень консервативна с точки зрения доступа к потоку: любая попытка изменить свойство в виджете пользовательского интерфейса (например, цвет, текст) вне потока пользовательского интерфейса выдает исключение.

Сейчас я смотрю на существующую программу на Swing, в которой есть окно с большим количеством пользовательских виджетов. Существует отдельный поток, который выполняет функцию мутации для каждого из этих виджетов, а функция мутации считывает значение некоторых вещей (например, цвета и значения меток) и записывает некоторые (например, изменяет цвета фона). Обратите внимание, что здесь нет никакой пользовательской рисования или чего-то в этом роде, просто куча изменений в вспомогательных виджетах, которые он содержит, в основном это JLabels.

В настоящее время это выполняется из отдельного потока, а не из потока событий Swing. Этот поток перебирает все 400 виджетов и вызывает мутатор для каждого. Обновления, кажется, работают правильно, но графический интерфейс не реагирует на ввод данных пользователем.

Если я возьму все это, которое будет длиться около 0,4 мсек вне потока Swing, и перенесу каждый вызов мутатора в invokeLater или invokeAndWait, пользовательский интерфейс будет намного более отзывчивым.

Я пытаюсь понять:

1) Иногда законно совершать все эти вызовы извне потока Swing?

2) Какое влияние оказывает на поток Swing и почему пользовательский интерфейс менее отзывчив, когда я вызываю его извне?

Ответы [ 5 ]

3 голосов
/ 16 сентября 2009

1) Является ли иногда допустимым делать все эти вызовы извне потока Swing?

Есть несколько исключений (установка текстового значения текстового поля, например, делает автоматическое проксирование EDT для вас) - но есть нет ситуаций, когда это лучше сделать. Если вы выполняете много обновлений, вы можете сделать их все за один вызов EDT (один вызов invokeLater ()) вместо отдельных вызовов, но даже такого рода пакетирование очень редко помогает. Длинный и короткий: выполняйте операции над компонентами Swing из EDT. Это включает в себя чтение и запись.

2) Какое влияние оказывает на поток Swing и почему пользовательский интерфейс менее отзывчив, когда я вызываю его извне?

Ну, EDT отвечает за обновление графического интерфейса. Если вы звоните извне, это не «менее отзывчиво» - фактические системные вызовы низкого уровня, которые обновляют пользовательский интерфейс, не происходят (вообще). Вероятно, в вашем приложении происходит то, что первоначальным разработчикам повезло и они меняют состояние компонента Swing, не создавая действительно неприятных условий гонки. Затем какое-то другое событие вызывает перерисовку в EDT, что приводит к обновлению компонента. Это может показаться «недостаточной отзывчивостью», но в действительности происходит «отсутствие обновления экрана».

EDT - это обычный поток, но он немного особенный, поскольку он работает в узком цикле, который обрабатывает сигналы, связанные с графическим интерфейсом (например, команды рисования). Семантеки размещения команд этих типов в EDT действительно очень отличаются от того, что мы обычно называем потоками Java (оно включает в себя отправку операций в насос сообщений).

Длинно и коротко - все те Javadoc, которые говорят «взаимодействуют только с объектами Swing в EDT», написаны по причине. Не связывайтесь с этим. Если вы хотите выполнить фоновую обработку, хорошо - но вы несете ответственность за передачу взаимодействия с компонентами J * обратно в EDT (чаще всего с использованием invokeLater ()).

3 голосов
/ 16 сентября 2009

От «Ничего» до периодически возникающих проблем, до «Все сломалось, потяните всех за работу в GUI!»

Основной (наиболее очевидный) визуальный эффект заключается в том, что если вы удерживаете поток графического интерфейса (например, когда кто-то нажимает кнопку, и вы делаете сон (5000) или что-то в этом роде), ваш графический интерфейс не перекрашивается. Это невозможно, потому что вы держитесь за единственную нить, которую вам разрешено пропустить! Это заставляет людей думать, что Java действительно медленная. Это неплохо, но достаточно легко запрограммировать, что многие люди, которые не беспокоятся о таких методах исследования, производят отгрузочные продукты.

Следующая самая большая проблема заключается в том, что когда вы рисуете экран в другом потоке (например, который передан в основной), он может иметь странное поведение. Swing уже слишком требователен к тому, как вы визуализируете ваши фреймы - выведите потоки как переменную!

Наконец, редко (или часто, если вы вызываете компонент колебания в узком цикле не того потока), вы можете столкнуться с потоком. Если это произойдет, может быть сгенерировано исключение (или нет), и что-то, вероятно, будет отображаться неправильно, но это может быть неочевидно.

2 голосов
/ 16 сентября 2009
  1. Здесь действительно нет исключений. Кевин частично прав - JTextComponent.setText () объявлен как потокобезопасный. Однако, глядя на код 1.6, он обеспечивает синхронизацию объекта документа и не использует EDT. Это нормально, если только другой компонент колебания (или что-то, что управляет компонентами колебания) не слушает объект документа. Избавьте себя от беспокойства об этом и просто всегда используйте EDT - как говорит Кевин, на самом деле нет ситуаций (о которых я знаю), чтобы поступить иначе.

  2. Трудно сказать, не копаясь в коде; поведение не определено. Если ваши фоновые задачи выполнялись долго (> несколько секунд), вы бы увидели обратный эффект - использование EDT сделает пользовательский интерфейс не отвечающим во время выполнения ваших задач.

К счастью, звучит так, будто правильное решение работает лучше для вас. :)

Раньше Sun говорил, что можно использовать другие потоки с компонентами, которые не были реализованы, но позже перечитаны:

Вопрос, связанный с переполнением стека

Посмотрите учебник по пользовательскому интерфейсу Sun по колебанию и параллелизму (я бы опубликовал ссылку, но это мой первый ответ на stackoverflow0.

1 голос
/ 16 сентября 2009

Для (1), как правило, все, что обновляет пиксели на экране, должно вызываться из Нити диспетчеризации событий (EDT) . Там, где некоторые JVM могут обрабатывать обновления извне EDT приемлемо, вы никогда не должны полагаться на эту работу, некоторые машины и другой внешний вид не будут работать приемлемо. Поведение не определено - и это может объяснить отсутствие реакции, которую вы видели.

1 голос
/ 16 сентября 2009

Основная проблема заключается в том, что не поточно-ориентированные объекты выполняются многопоточным способом. Даже такая простая вещь, как чтение HashMap, может попасть в бесконечный цикл. Поскольку AWT использует блокировки (плохо), вы также можете получить тупики. Однако вам, вероятно, это сойдет с рук, хотя вы можете обнаружить, что обновленная версия Java внезапно вызывает проблемы на некоторых клиентских компьютерах.

(Кстати: это поток рассылки событий AWT, а не Swing.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...