EventQueue.invokeLater - czy to jest potrzebne ?

0

Mam książkę "Java podstawy wydanie VIII" i jak teraz czytam o tworzeniu aplikacji okienkowych to autor pisze żeby pisać (nie wyjaśniając zbytnio dlaczego):

public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
               ..........................................
            }
        });
    }

I tak jest w każdym jego kodzie aplikacji okienkowej.

Tak się normalnie pisze w javie tworząc aplikacje okienkowe czy się to pomija i wykorzystuje tylko jak tworzymy aplikację wielowątkową ?

8

Metoda ta powoduje, że kod, który jest umieszczony w metodzie run zostanie wykonany na pewno przez wątek uruchomiony przez klasy Swing - konkretniej przez wątek służący do obsługi interfejsu graficznego.
Powodem tego jest to iż kod biblioteki Swing nie jest wielowątkowy. Ponieważ obsługą interfejsu zajmuje się już jeden konkretny wątek, to wszystko co zmienia ten interfejs musi być uruchomione z tego właśnie wątku. W wywołaniu o które pytasz przekazujesz do invokeLater() klasę poruszaną przez ten sam wątek. To co przekazujesz, to klasa anonimowa implementująca interfejs Runnable.
Krótko mówiąc do wątku Swing przekazujesz receptę jak coś zrobić i zrobić to za pomocą wywołań metod klas Swing. Tym właśnie zajmuje się invokeLater().
Wątek uruchamiający metodę main() nie ma nic wspólnego z wątkiem Swing i przetwarzaniem jego kolejki zdarzeń. Stąd jednym ze sposobów przekazywania czegoś do obsługi interfejsu jest asynchroniczne zlecenie wykonania czegoś przez niego.
Najbardziej czystym sposobem rozpoczęcia pracy aplikacji sterowanej zdarzeniami jest jak najszybsze przekazanie sterowania do kodu okienkowego, któremu musimy przekazać zlecenie stworzenia interesujących nas elementów graficznych. Stąd wystarczy jedno wywołanie invokeLater() i na tym kod metody main() mógłby się już zakończyć (z kodem powrotu == 0). Od momentu rozpoczęcia pracy grafiki wszystko co program zrobi będzie już reakcją na działania użytkownika tej aplikacji.
Oczywiście można opóźniać zakończenie metody main() i zlecić jej wykonania dodatkowych zadań - czy to przed czy po wywołaniu kodu Swing rozpoczynającego życie graficznej otoczki aplikacji. Wtedy traktujemy to po prostu jako dodatkowy wątek roboczy. Wszystkie inne wątki muszą być skonstruowane jako reakcja aplikacji. Można w tym celu bawić się klasą Thread, albo SwingWorker. Dopóki jednak to nie nastąpi, a kod metody main() zakończy się, to aplikacja staje się ekranowa i całkowicie jednowątkowa.

W przypadku apletu zamiast invokeLater() trzeba użyć invokeAndWait(), która nie zleca wykonania czegoś asynchronicznie, ale czeka z powrotem sterowania aż do rzeczywistego wykonania zawartego w Run() kodu. Jest tak ponieważ zakończenie wątku uruchamiającego init(), jeżeli zakończy się przed pełną konstrukcją grafiki, spowoduje u części przeglądarek crash.

W każdym razie kiedy kod okienkowy własnej aplikacji zacznie być poruszany przez Swinga, wtedy dopiero ten kod może tworzyć własne nowe wątki dla długotrwałych zadań. Ale wtedy najlepiej użyć klasy SwingWorker. Jeżeli z takiego długotrwałego zadania będziemy chcieli poruszyć coś okienkowego, to znowu trzeba to zlecić wątkowi Swing przez invokeLater(), albo invokeAndWait().

0

Trochę to skomplikowane - dzięki :)

0
Olamagato napisał(a)

Metoda ta powoduje, że kod, który jest umieszczony w metodzie run (...)

Czyli rozumie, że jak sobie zrobię:

EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.setContentPane( new CoolPanel() );
frame.setVisible(true);
}
}

to w CoolPanel'u nie muszę znowu wywoływać EventQueue.invokeLater'a, (nie używam SwinkWorker).,
bo to i tak się w tym wątku Swinga wykona....

Zacząłem się nad tym zastanawiać, bo w moim projekcie właśnie tworzę frame'a w metodzie run wątku Swinga (przez invokeLater), nataomiast problem pojawia się gdy tworzę:

final JSplitPane middleSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, p1, p2);
middleSplit.setDividerLocation(0.5);

gdzieś w kolejnych panelach... i nie ustawia mi tego 50% podziału... więc po chwili zastanowienia zrobiłem EventQueue.invokeLater'a... i działa :)

tylko, że to nie ma sensu, bo wszystko wywołuje się i tak w wątku Swinga, chyba że się mylę :)

...czytałem też, że wiele osób robi to w ten sposób lub w inny podobny, może lepszy... Polega on na tym, żeby wywołać validate() na frameu (tylko, że w tej klasie nie mam dostępu do freamea, a żeby go tam przekazać... to ohoho długa droga i zabawa... :( a validate na middleSplit, ani też na panelu go zawierającym, nic nie daje :( ]

P.S. wielkie dzięki ;) za rzeczowe wyjaśnienie poprzedniego wątku
i liczę na miłe wyjaśnienie ;)...

Pozdrawiam

0

bo swoją drogą, robiąc:

EventQueue.invokeLater(new Runnable()
{
public void run()
{
middleSplit.setDividerLocation(0.5);
}
}

to dwa wątki swinga będą działały ?? (1-den z funkcji main, a 2-gi ten tutaj)
czy zadania są kolejkowane i wykonane po kolei...

jeśłi kolejkowane... to rozumie, że
to to jest tylko przypadek, że to middleSplit.setDividerLocation(0.5); wykona się dokładnie po tym jak interfejs zostanie zbudowany... Czy takie jest właśnie celowe zamierzenie implementacji Swinga??

Pozdrawiam raz jeszcze

0

przepraszam, rąbnąłem się przy kopij-wklej, w ostatnim zdaniu
więc nie bierzcie go pod uwagę :D (a jest już późńo :| :D)

  1. jeśli działają dwa wątki to, to jest tylko przypadek, że to middleSplit.setDividerLocation(0.5); wykona się dokładnie po tym jak interfejs zostanie zbudowany... (gdyż, kolejność wywoływania się wątków może być dowolna)

  2. jeśli są kolejkowane (i w wolnym "czasie" swing wykonuje je po kolei) to rozumie, że
    to wszystko jest ok.
    Czy takie jest właśnie celowe zamierzenie implementacji Swinga??

To drugie ma sens... ale potrzeba mi było wiele czasu, żeby to rozgryźć? :D

Czy słusznie? Mam nadzieje, że dacie znać ;)

0

Tak jak Olamagato napisał, działa tylko jeden wątek swinga - invokeLater dodaje tylko do kolejki.

Jeżeli się nie używa wspomnianej metody (tylko "na żywca" edytuje kontrolki swinga) to zazwyczaj wszystko niby działa, ale co jakiś czas się wysypuje.

Żeby mieć pewność (czytaj: żeby program się zawsze wysypywał przy odwołaniu ze złego wątku), możesz użyć np. tej klasy:

http://pokazywarka.pl/careful-repaint-manager/

0

Wróciłem do tego wątku, bo widzę, napotkałem kolejny problem... (i tutaj się zainteresowałem tym wspomnianym wyżej SwingWorkerem - hmm - tego mi chyba będzie trzeba...)

Ale wracając do problemu:

otóż napisałem sobie taki oto fragment, w celu pozamykania wszystkich "badziewnych" okienek

            //CloseFrame extends JFrame implements Closable
            Iterator<ClosableFrame> iterator = xset.iterator();
	
	while(iterator.hasNext()) {
		ClosableFrame frame = iterator.next();
		frame.close();
                    frame.dispose();
	}

ale niestety, przy dwóch okienkach w xsecie, program się wysypuje "słowami":
Exception in thread "AWT-EventQueue-0" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at ... i dalej jest wskazanie na miejsce w kodzie: ClosableFrame frame = iterator.next();

oczywiście najpierw zrobiłem tak:

            Iterator<ClosableFrame> iterator = xset.iterator();
	
	while(iterator.hasNext()) {
		final ClosableFrame frame = iterator.next();
		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() { 
		                frame.close();
                                    frame.dispose();
                            }
		});
	}

program się nie wysypuje, no ale dalej nie mam pewności, że się nic nie wysypie później (np. przy większej ilości okienek)
((bo przecież przetwarzanie kolejki swinga może się skończyć wcześniej niż pociągnięcie z iteratora -> i tak z pewnością się stanie!!))

Co więc powinienem zrobić...
Z góry dzięki za pomoc.

karolex

0

Podałem Ci kod, dzięki któremu miałbyś pewność pod kątem tych wyjątków...

0
tomkiewicz napisał(a)

Podałem Ci kod, dzięki któremu miałbyś pewność pod kątem tych wyjątków...

ok, zagłębiam się więc w lekturę raz jeszcze... dzięki.

0
karolex napisał(a)
tomkiewicz napisał(a)

Podałem Ci kod, dzięki któremu miałbyś pewność pod kątem tych wyjątków...

ok, zagłębiam się więc w lekturę raz jeszcze... dzięki.

ok... chyba się nie rozumiemy... co do tych wątków to ja już pewność mam ... teraz zadałem inny problem (nie wiem czy rozumiesz)

Co ciekawe, ten programik nie odpowiada na mój ostatni post w ogóle... [bo ja odpalam ten kod (pętli while z iteratorem w środku) w wątku swinga) - przetestowane nawet Twoją klasą]

ok, mój błąd, powinienem napisać w innym wątku... do zobaczenia w innym wątku... adios

P.S. Dzięki za fatygę

0

sprawa rozwiązana, i śmiesznie, bo w ogóle tu nie chodziło o fragment SwingUtylities.invokeLater(... (co więcej ten fragment jest zbędny, co się zgadza z moją wcześniejszą argumentacją :P)

w razie czego podaję link do odpowiedzi:
jak poprawnie niszczyć elementy kolekcji...

1 użytkowników online, w tym zalogowanych: 0, gości: 1