Правило єдиного потоку Java, Блог лише про Java

Вчимося програмувати на Java з нуля

Правило єдиного потоку Java

єдиного

Кожна програма Java запускається з методу main(), який працює в головному потоці. У програмі Swing головний потік живе короткочасно. Він планує конструювання інтерфейсу користувача в потоці диспетчера подій і завершує свою роботу.

Після створення інтерфейсу користувача потік диспетчера подій обробляє повідомлення про події на зразок викликівactionPerformed() абоpaintComponent(). Інші потоки, такі як потік, що відправляє події в чергу, працюють "за лаштунками", але ці потоки невидимі прикладному програмісту.

Існує кілька винятків із цього правила:

  • Ви можете абсолютно безпечно додавати та видаляти слухачі подій у будь-якому потоці. Звісно, ​​методи слухачів будуть викликані з потоку диспетчера подій.
  • Невелика кількість методівSwing є безпечними до потоків. Вони спеціально помічені в документації по API пропозицією "Цей метод є thread safe, але не найбільший метод Swing methods are not" (Цей метод безпечний до потоків, хоча більшість методів Swing такими не є). Найбільш корисні з таких безпечних до потоків методів:
  • JTextComponent.setText()
  • JTextArea.insert()
  • JTextArea.append()
  • JTextArea.replaceRange()
  • JComponent.repaint()
  • JComponent.revalidate()

Однак, щоб ініціювати компонуванняJFrame, вам доведеться викликатиvalidate(), тому щоJFrame - цеComponent, а неJComponent.

Історично правило єдиного потоку було ліберальнішим. Будь-який потік міг конструювати компоненти,встановлювати їх властивість і додавати в контейнери - до тих пір, поки жоден з компонентів не було реалізовано. Компонент реалізується, коли починає приймати події перемальовки та перевірки достовірності. Це відбувається, коли виконується викликsetVisible(true) абоpack() для цього компонента, або коли компонент додається до вже реалізованого контейнера.

Ця версія правила одного потоку була зручною. Вона дозволяла створювати графічний інтерфейс користувачав методі main() і потім викликати setVisible(true) на фреймі верхнього рівня програми. Не було потреби у стомливому плануванні запуску черезRunnable у потоці диспетчера подій.

На жаль, деякі реалізатори компонентів не приділили належної уваги тонкощі вихідного правила одного потоку. Вони запускали дії в потоці диспетчера подій, не переймаючись навіть перевіркою того, чи реалізований компонент. Наприклад, якщо викликається setSelectionStart() або setSelectionEnd() наJTextComponent, то переміщення знака вставки плануються в потоці диспетчера подій, навіть якщо компонент невидимий.

Виявити та виправити ці проблеми зовсім нескладно, але проектувальники Swing пішли шляхом найменшого опору. Вони оголосили, що звертатися до компонентів із будь-якого потоку, який не є потоком диспетчера подій, небезпечно. А тому вам потрібно конструювати інтерфейс користувача в потоці диспетчера подій, використовуючи викликEventQueue.invokeLater(), який ви вже не раз бачили в наших прикладах програм.

Звичайно, існує маса програм, які не дотримуються обережності і керуються старими уявленнями про правило одного потоку, ініціалізуючи інтерфейс користувача в головному потоці.

Ці програмисхильні до деякого ризику, пов'язаного з тим, що визначення дії з ініціалізації інтерфейсу користувача викличуть конфлікт з діями головного потоку. Ви навряд чи захочете виявитися одним з небагатьох "щасливчиків", які зіткнуться з неприємностями і будуть змушені витрачати час на налагодження помилок, що нерегулярно відтворюють, пов'язаних з потоками. Тому краще просто дотримуватися правила єдиного потоку.