Подтвердить что ты не робот

Java: Swing Library & Thread Safety

Я часто слышал критику отсутствия безопасности потоков в библиотеках Swing. Тем не менее, я не уверен, что я буду делать в своем собственном коде, что может вызвать проблемы:

В каких ситуациях вступает в игру тот факт, что Swing не является потокобезопасным?

Чего мне следует избегать?

4b9b3361

Ответ 1

  • Никогда не выполняйте длительные задачи в ответ на кнопку, событие и т.д., поскольку они находятся в потоке событий. Если вы заблокируете поток событий, ENTIRE GUI будет полностью не отвечать, в результате ДЕЙСТВИТЕЛЬНО разозлили пользователей. Вот почему Swing кажется медленным и твердым.

  • Используйте Threads, Executors и SwingWorker для запуска задач NOT ON EDT (поток отправки событий).

  • Не обновляйте и не создавайте виджеты вне EDT. Почти единственный вызов, который вы можете сделать за пределами EDT, это Component.repaint(). Используйте SwingUtilitis.invokeLater для обеспечения выполнения определенного кода на EDT.

  • Используйте EDT Debug Techniques и умный внешний вид (например, Substance, который проверяет нарушение EDT)

Если вы будете следовать этим правилам, Swing может сделать некоторые очень привлекательные и RESPONSIVE GUIs

Пример некоторого ДЕЙСТВИТЕЛЬНО удивительного Swing UI: Palantir Technologies. Примечание. Я НЕ РАБОТАЮ для них, просто пример потрясающего колебания. Позор не публичная демонстрация... Их blog тоже хорош, разрежен, но хорош

Ответ 2

Это один из тех вопросов, который заставляет меня радоваться, что я купил книгу Робинсона и Воробьева о Swing.

Все, что обращается к состоянию java.awt.Component, должно выполняться внутри EDT с тремя исключениями: что-либо, специально задокументированное как потокобезопасное, например repaint(), revalidate() и invalidate(); любой компонент в пользовательском интерфейсе, который еще не реализован; и любой компонент в апплете до того, как был вызван Applet start().

Методы, специально сделанные в потокобезопасности, настолько необычны, что часто достаточно просто запомнить те, которые есть; вы также можете уклониться от предположения, что таких методов нет (совершенно безопасно обернуть вызов перерисовки в SwingWorker, например).

Реализованный означает, что Компонент является либо контейнером верхнего уровня (например, JFrame), на котором был вызван любой из setVisible(true), show() или pack(), или он был добавлен в реализованный компонент. Это означает, что для создания вашего пользовательского интерфейса в основном(), как и во многих примерах учебников, совершенно отлично, поскольку они не вызывают setVisible(true) в контейнере верхнего уровня до тех пор, пока каждый компонент не будет добавлен к нему, настройки шрифтов и границ, и т.д.

По аналогичным причинам совершенно безопасно создавать свой API-интерфейс апплета в методе init(), а затем вызывать start() после его создания.

Обменивать последующие изменения компонентов в Runnables для отправки на invokeLater() становится легко получить сразу после выполнения всего несколько раз. Единственное, что я нахожу раздражающим, - это считывание состояния компонента (например, someTextField.getText()) из другого потока. Технически это должно быть завернуто в invokeLater(); на практике это может сделать код уродливым быстро, и я часто не беспокоюсь, или я стараюсь захватить эту информацию на начальном этапе обработки событий (как правило, подходящее время для этого в большинстве случаев в любом случае).

Ответ 3

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

Итак, все манипуляции Swing на EDT. Обратите внимание, что EDT - это не тот поток, на который вызван основной вызов, поэтому запустите свои (простые) приложения Swing, такие как этот шаблон:

class MyApp {
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() { public void run() {
            runEDT();
        }});
    }
    private static void runEDT() {
        assert java.awt.EventQueue.isDispatchThread();
        ...

Ответ 4

Альтернативой использованию интеллектуальных скинов, таких как вещество, является создание следующего метода утилиты:

public final static void checkOnEventDispatchThread() {
    if (!SwingUtilities.isEventDispatchThread()) {
        throw new RuntimeException("This method can only be run on the EDT");
    }
}

Вызовите его в каждом методе, который вы пишете, который должен быть включен в поток отправки событий. Преимущество этого заключается в том, чтобы очень быстро отключить и включить проверки системы, например, возможно, удалить это в процессе производства.

Заметьте, что интеллектуальные скины могут, конечно, обеспечивать дополнительное покрытие, а также только это.

Ответ 5

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

У меня не было никаких проблем, следуя моим советам выше. Есть некоторые обстоятельства, когда вы можете "качать" из других потоков, но я никогда не нашел нужды.

Ответ 6

Если вы используете Java 6, то SwingWorker, безусловно, самый простой способ справиться с этим.

В основном вы хотите убедиться, что все, что изменяет пользовательский интерфейс, выполняется в EventDispatchThread.

Это можно найти с помощью метода SwingUtilities.isEventDispatchThread(), чтобы сообщить вам, находитесь ли вы в нем (как правило, не очень хорошая идея - вы должны знать, какой поток активен).

Если вы не находитесь на EDT, вы используете SwingUtilities.invokeLater() и SwingUtilities.invokeAndWait() для вызова Runnable на EDT.

Если вы обновляете интерфейс, а не на EDT, вы получаете невероятно странное поведение. Лично я не считаю это недостатком Swing, вы получаете хорошую эффективность, не синхронизируя все потоки, чтобы обеспечить обновление пользовательского интерфейса - вам просто нужно запомнить это оговорку.

Ответ 7

Фраза "thread-unsafe" звучит так, будто есть что-то по своей сути плохое (вы знаете... "безопасно" - хорошо, "небезопасно" - плохо). Реальность заключается в том, что безопасность потоков происходит за счет затрат - потокобезопасные объекты часто сложнее реализовать (и Swing достаточно сложна, даже если есть).

Кроме того, безопасность потоков достигается либо с помощью стратегий блокировки (медленного), либо сравнения и замены (комплексного). Учитывая, что GUI взаимодействует с людьми, которые, как правило, непредсказуемы и трудно синхронизируются, многие инструментальные средства решили передать все события с помощью одного насоса событий. Это справедливо для Windows, Swing, SWT, GTK и, возможно, для других. На самом деле, я не знаю ни одного инструментария GUI, который действительно потокобезопасен (что означает, что вы можете управлять внутренним состоянием своих объектов из любого потока).

Что обычно делается, так это то, что графические интерфейсы обеспечивают способ справиться с потоком-небезопасностью. Как отмечали другие, Swing всегда предоставлял несколько упрощенные SwingUtilities.invokeLater(). Java 6 включает превосходный SwingWorker (доступный для предыдущих версий Swinglabs.org). Существуют также сторонние библиотеки, такие как Foxtrot для управления потоками в контексте Swing.

Известность Swing заключается в том, что дизайнеры взяли на себя легкий подход, предполагая, что разработчик пойдет правильно, а не остановит EDT или изменит компоненты вне EDT. Они заявили, что их политика потоковой передачи громко и ясно, и разработчикам следует следовать за ней.

Это тривиально, чтобы каждый API-интерфейс swing отправлял задание в EDT для каждого набора свойств, invalidate и т.д., что сделало бы его потокобезопасным, но ценой значительных замедлений. Вы даже можете сделать это самостоятельно, используя АОП. Для сравнения, SWT выдает исключения, когда к компоненту обращаются из-за неправильного потока.

Ответ 8

Здесь шаблон для makng swing thread-freindly.

Действие Sublass (MyAction) и сделайте его обработанным. Создайте конструктор с помощью NAME строки.

Дайте ему абстрактный метод actionImpl().

Пусть это выглядит как (предупреждение псевдокода!)

doAction(){
new Thread(){
   public void run(){
    //kick off thread to do actionImpl().
       actionImpl();
       MyAction.this.interrupt();
   }.start();  // use a worker pool if you care about garbage.
try {
sleep(300);
Go to a busy cursor
sleep(600);
Show a busy dialog(Name) // name comes in handy here
} catch( interrupted exception){
  show normal cursor
}

Вы можете записать время, затрачиваемое на выполнение задачи, и в следующий раз ваше диалоговое окно может показать приемлемую оценку.

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

Ответ 9

Обратите внимание, что даже интерфейсы модели не являются потокобезопасными. Размер и содержание запрашиваются с помощью отдельных методов get и поэтому нет возможности их синхронизации.

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

Обновление состояния модели всегда в EDT позволяет избежать этого.

Ответ 10

invokeLater() и invokeAndWait() действительно ДОЛЖНЫ использоваться, когда вы выполняете какое-либо взаимодействие с компонентами GUI из любого потока, который НЕ является EDT.

Он может работать во время разработки, но, как и большинство параллельных ошибок, вы начинаете видеть странные исключения, которые кажутся совершенно не связанными, и происходят без детерминированности - обычно замечены ПОСЛЕ того, что вы отправили настоящие пользователи. Нехорошо.

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

Да, он получает уродливую упаковку каждого метода обратно в EDT в экземпляре Runnable, но это Java для вас. Пока мы не закрываем, вам просто нужно жить с ним.

Ответ 11

Для более подробной информации о потоковой передаче, приручение потоков Java от Allen Holub - это более старая книга, но отличное чтение.

Holub, действительно способствует гибкому пользовательскому интерфейсу и подробным примерам и способам устранения проблем.

http://www.amazon.com/Taming-Java-Threads-Allen-Holub/dp/1893115100 http://www.holub.com/software/taming.java.threads.html

В конце концов, полюбите раздел "Если я был королем".