Swing резба и нишка за изпращане на събития

Предишна 1 2 3 4 5 Страница 5 Страница 5 от 5

Поддържане на безопасна нишка Swing

Последната стъпка при създаването на Swing GUI е да го стартирате. Правилният начин да стартирате Swing GUI днес се различава от първоначално предписания подход на Sun. Ето цитат от документацията на Sun отново:

След като е реализиран Swing компонент, целият код, който може да повлияе или зависи от състоянието на този компонент, трябва да бъде изпълнен в нишката за изпращане на събития.

Сега изхвърлете тези инструкции през прозореца, защото около пускането на JSE 1.5 всички примери на сайта на Sun се промениха. Оттогава е малко известен фактът, че трябва винаги да имате достъп до компонентите Swing на нишката за изпращане на събития, за да осигурите безопасността на техните нишки / еднонишковия достъп. Причината за промяната е проста: докато вашата програма може да осъществи достъп до Swing компонент от нишката за изпращане на събития, преди компонентът да бъде реализиран, инициализирането на потребителския интерфейс на Swing може да задейства нещо, което да се изпълни в нишката за изпращане на събития след това, защото компонент / потребителски интерфейс очаква да изпълни всичко в нишката за изпращане на събития. Ако графичните интерфейси се изпълняват на различни нишки, това прекъсва еднонишкото моделиране на Swing.

Програмата в Листинг 5 не е съвсем реалистична, но служи за да изтъкна моята точка.

Листинг 5. Достъп до състоянието на компонента Swing от множество нишки

import java.awt.*; import java.awt.event.*; import javax.swing.*; public class BadSwingButton { public static void main(String args[]) { JFrame frame = new JFrame("Title"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Press Here"); ContainerListener container = new ContainerAdapter() { public void componentAdded(final ContainerEvent e) { SwingWorker worker = new SwingWorker() { protected String doInBackground() throws InterruptedException { Thread.sleep(250); return null; } protected void done() { System.out.println("On the event thread? : " + EventQueue.isDispatchThread()); JButton button = (JButton)e.getChild(); String label = button.getText(); button.setText(label + "0"); } }; worker.execute(); } }; frame.getContentPane().addContainerListener(container); frame.add(button, BorderLayout.CENTER); frame.setSize(200, 200); try { Thread.sleep(500); } catch (InterruptedException e) { } System.out.println("I'm about to be realized: " + EventQueue.isDispatchThread()); frame.setVisible(true); } }

Забележете, че изходът показва някакъв код, изпълняващ се в основната нишка, преди потребителският интерфейс да бъде реализиран. Това означава, че кодът за инициализация се изпълнява в една нишка, докато другият код на потребителския интерфейс се изпълнява в нишката за изпращане на събития, което прекъсва еднонишковия модел за достъп на Swing:

> java BadSwingButton On the event thread? : true I'm about to be realized: false

Програмата в Листинг 5 ще актуализира етикета на бутона от слушателя на контейнера, когато бутонът бъде добавен към контейнера. За да направите сценария по-реалистичен, представете си потребителски интерфейс, който "брои" етикети в него и използва броя като текст в заглавието на границата. Естествено, ще трябва да актуализира текста на заглавието на границата в нишката за изпращане на събития. За да улесни нещата, програмата просто актуализира етикета на един бутон. Макар да не е реалистична по функция, тази програма показва проблема с всяка програма Swing, която е написана от началото на времето на Swing. (Или поне всички, които следват препоръчителния модел на резби, намерен в javadocs и онлайн уроците от Sun Microsystems, и дори в моите собствени ранни издания на Swing книги за програмиране.)

Суинг резбата направена правилно

Начинът да накарате Swing на резба да е да забравите оригиналното изречение на Sun. Не се притеснявайте дали компонентът е реализиран или не. Не се притеснявайте да се опитвате да определите дали е безопасно да получите достъп до нещо извън нишката за изпращане на събития. Никога не е така. Вместо това създайте целия потребителски интерфейс в нишката за изпращане на събития. Ако поставите цялото повикване за създаване на потребителски интерфейс във EventQueue.invokeLater()всички достъпи по време на инициализацията, гарантирано ще бъдат извършени в нишката за изпращане на събития. Толкова е просто.

Листинг 6. Всичко на мястото си

import java.awt.*; import java.awt.event.*; import javax.swing.*; public class GoodSwingButton { public static void main(String args[]) { Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Title"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JButton button = new JButton("Press Here"); ContainerListener container = new ContainerAdapter() { public void componentAdded(final ContainerEvent e) { SwingWorker worker = new SwingWorker() { protected String doInBackground() throws InterruptedException { return null; } protected void done() { System.out.println("On the event thread? : " + EventQueue.isDispatchThread()); JButton button = (JButton)e.getChild(); String label = button.getText(); button.setText(label + "0"); } }; worker.execute(); } }; frame.getContentPane().addContainerListener(container); frame.add(button, BorderLayout.CENTER); frame.setSize(200, 200); System.out.println("I'm about to be realized: " + EventQueue.isDispatchThread()); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }

Стартирайте го сега и горната програма ще покаже, че и инициализацията, и кодът на контейнера се изпълняват в нишката за изпращане на събития:

> java GoodSwingButton I'm about to be realized: true On the event thread? : true

В заключение

Допълнителната работа за създаване на вашия потребителски интерфейс в нишката за изпращане на събития в началото може да изглежда ненужна. В края на краищата всички го правят по друг начин от началото на времето. Защо да си правим труда да се променя сега? Работата е там, че винаги сме го правили погрешно. За да сте сигурни, че вашите Swing компоненти са достъпни правилно, винаги трябва да създавате целия потребителски интерфейс в нишката за изпращане на събития, както е показано тук:

Runnable runner = new Runnable() { public void run() { // ...create UI here... } } EventQueue.invokeLater(runner);

Преместването на вашия код за инициализация в нишката за изпращане на събития е единственият начин да се гарантира, че вашите графични интерфейси на Swing са безопасни за нишки. Да, първоначално ще се чувства неловко, но напредъкът обикновено го прави.

Джон Зуковски играе с Java вече над 12 години, като отдавна е изоставил мисленето си на C и X-Windows. С излезлите 10 книги по теми от Swing до колекции до Java SE 6, Джон сега прави стратегически технологични консултации чрез своя бизнес, JZ Ventures, Inc ..

Научете повече за тази тема

  • Научете повече за програмирането на Swing и нишката за изпращане на събития от един от главните разработчици на десктоп Java: Chet Haase за максимизиране на Swing и Java 2D (подкаст JavaWorld Java Technology Insider, август 2007 г.).
  • „Персонализирайте SwingWorker, за да подобрите графичните интерфейси на Swing“ (Yexin Chen, JavaWorld, юни 2003 г.) се задълбочава в някои от предизвикателствата на Swing резбата, обсъдени в тази статия, и обяснява как персонализираният SwingWorkerможе да осигури мускулите да заобиколят тях.
  • „Java и обработка на събития“ (Todd Sundsted, JavaWorld, август 1996 г.) е буквар за обработка на събития около AWT.
  • „Ускоряване на известието на слушателя“ (Робърт Хейстингс, JavaWorld, февруари 2000 г.) въвежда спецификацията JavaBeans 1.0 за регистрация и уведомяване за събития.
  • „Постигане на силна производителност с нишки, част 1“ (Jeff Friesen, JavaWorld, май 2002 г.) представя Java нишки. Вижте Част 2 за отговор на въпроса: Защо се нуждаем от синхронизация?
  • „Изпълнение на задачи в нишки“ е откъс от JavaWorld от Java Concurrency in Practice (Brian Goetz, et al., Addison Wesley Professional, май 2006 г.), който насърчава програмирането на нишки, базирано на задачи, и въвежда рамка за изпълнение за управление на задачите.
  • „Threads and Swing“ (Hans Muller and Kathy Walrath, април 1998 г.) е една от най-ранните официални справки за Swing резби. Включва известното вече (и погрешно) „правило с една нишка“.
  • Създаването на GUI с JFC / Swing е изчерпателната страница с уроци по Java за програмиране на Swing GUI.
  • „Съвпадение в суинг“ е урок по пътеката на Суинг, който включва въведение в SwingWorkerкласа.
  • JSR 296: Swing Application Framework в момента е в процес на спецификация. Вижте също „Използване на Swing Application Framework“ (John O'Conner, Sun Developer Network, юли 2007 г.), за да научите повече за тази следваща стъпка в еволюцията на Swing GUI програмирането.
  • Цялата справка за Java AWT (John Zukowski, O'Reilly, март 1997 г.) е достъпна безплатно от онлайн каталога O'Reilly.
  • Окончателното ръководство на Джон за Java Swing, трето издание (Apress, юни 2005 г.) е напълно актуализирано за Java Standard Edition версия 5.0. Прочетете глава за предварителен преглед от книгата точно тук, на JavaWorld !
  • Посетете JavaWorld Swing / GUI изследователския център за повече статии за Swing програмиране и разработка на десктоп Java.
  • Също така разгледайте форумите за разработчици на JavaWorld за дискусии и въпроси и въпроси, свързани със Swing и Java програмирането на работния плот.

Тази история, "Swing резби и нишка за изпращане на събития", първоначално е публикувана от JavaWorld.