Na forum 4programmers.net korzystamy z plików cookies. Część z nich jest niezbędna do funkcjonowania
naszego forum, natomiast wykorzystanie pozostałych zależy od Twojej dobrowolnej zgody, którą możesz
wyrazić poniżej. Klikając „Zaakceptuj Wszystkie” zgadzasz się na wykorzystywanie przez nas plików cookies
analitycznych oraz reklamowych, jeżeli nie chcesz udzielić nam swojej zgody kliknij „Tylko niezbędne”.
Możesz także wyrazić swoją zgodę odrębnie dla plików cookies analitycznych lub reklamowych. W tym celu
ustaw odpowiednio pola wyboru i kliknij „Zaakceptuj Zaznaczone”. Więcej informacji o technologii cookie
znajduje się w naszej polityce prywatności.
Mam za zadanie stworzyć prosty interfejs użytkownika, w którym naciśnięcie przycisku uruchamia kilka wątków, które w wypisują odpowiednie treści w polu tekstowym. Jak się tworzy komunikację między wątkami (czy szerzej, klasami spoza klasy implementującej okno), a komponentami okna?
Watek jest obiektem klasy rozszerzającej Thread, lub implementującej interfejs Runnable. (ja używam tego drugiego sposobu).
Ponieważ jest to "zwykła" klasa, to możesz do niej przekazać obiekt pola tekstowego, lub całej formatki przy pomocy konstruktora, lub settera.
Np.
Masz obiekt okno, który ma w sobie pole.
Tworzysz obiekt Wątek, implementujący Runnable.
Wywolujesz Watek.setPole(pole);
A potem:
Thread starter = new Thread(Wątek);
starter.start();
W metodzie Wątek.run() robisz pole.setText("hello world");
i tyle.
Pozdrawiam.
Usunąć wpis?
Tej operacji nie będzie można cofnąć.
automatix
automatix
0
Hej
Jeżeli zamierzasz z wątków odwoływać się np do tego samego komponentu, warto utworzyć metodę oznaczoną słowem synchronized np.
Kopiuj
classObslugaPrzyciskow implements Runnable
{private JTextField field;public ObsługaPrzyciskow(JTextField jt){
field = jt;}publicvoidrun()//implementacja metody z interfejsu Runnable, chyba tak się nazywa{for(int i=0; i<1000; i++){update();try{ Thread.sleep(1000);}catch(Exception ex){}}}private synchronized voidupdate(){
field.setText(field.getText()+" "+"jakiś nowy tekst");}}
Napisałem to z głowy to mogłem gdzieś się pomylić, ale mniej więcej taka koncepcja.
Potem tworzysz
Kopiuj
Thread t =newThread(new ObsługaPrzyciskow());
t.start();
Jeśli chodzi o Swinga to nie należy używać zwykłych wątków tylko ładować SwingWorkery: http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html . GUI powinno aktualizować tylko z poziomu EDT, inaczej mogą powstawać trudne do zlokalizowania błędy. Swing nie jest thread-safe, nie udało się zrobić swego czasu wielowątkowego Swinga i jak na razie nic mi nie wiadomo, żeby miało się to zmienić.
Jeśli chodzi o Swinga to nie należy używać zwykłych wątków tylko ładować SwingWorkery: http://download.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html . GUI powinno aktualizować tylko z poziomu EDT, inaczej mogą powstawać trudne do zlokalizowania błędy. Swing nie jest thread-safe, nie udało się zrobić swego czasu wielowątkowego Swinga i jak na razie nic mi nie wiadomo, żeby miało się to zmienić.
oracle.com napisał(a)
SwingWorker is only designed to be executed once. Executing a SwingWorker more than once will not result in invoking the doInBackground method twice.
A istnieje jakaś wersja która by działała cały czas w tle? Ja potrzebuję stworzyć wątek który cały czas wypisuje przypisaną mu literę synchronizując się z innymi wątkami (np. żeby A i B były wyświetlane na zmianę) więc SwingWorker niespecjalnie się do tego nadaje.
Hm, to powiedz mi może, co ja źle robię. Stworzyłem coś takiego:
Kopiuj
publicclassMainwindowextendsJFrame{privateclassLetterWriterextendsSwingWorker<Object,Character>{CharacterLetter;publicLetterWriter(Character let){Letter= let;}@OverrideprotectedObjectdoInBackground()throwsException{while(true){if(Kontrolka.CheckAvalability(Letter)){System.out.println(Letter.toString()+" Printing");publish(Letter);Kontrolka.notifyAll();}else{System.out.println(Letter.toString()+" Waiting...");Kontrolka.wait();}}}protectedvoidprocess(List<Character> chunks){for(Character let : chunks){OutputTextArea.append(let.toString());}}}}
Wątek uruchamiam za pomocą metody execute() w funkcji obsługującej naciśnięcie przycisku. Problem polega na tym, że po wywołanu funkcji notifyAll() wątek nie powtarza już ani razu swojego przebiegu. wynikiem tego programu jest wypisanie tylko jednego A. (CheckAvalability() na razie zawsze zwraca true)
Jeżeli wywalę notifyAll(), to owszem działa ale strasznie spowalnia interfejs, nie mówiąc o tym, że nie potrafię przerwać tego wątku metodą cancel()
Wrzucam całą klasę Mainwindow. Dodam że poprawienie tej pętli nie pomogło. Dalej nie potrafię przerwać tego wątku. Mógłbyś tez przybliżyć dlaczego powinienem zmienić na char[]?
Zamiana na String całości od razu ze względów wydajnościowych. Podejrzewam, że metoda append(String) jest dość kosztowna, więc lepiej wywoływać ją jak najmniej razy.
Uruchomiłem ten kod u siebie (Ubuntu, OpenJDK) i raczej działa poprawnie. Przeładowałem metodę done() w SwingWorkerze i widzę że się wywołuje gdy kliknę przycisk Stop. Widzę także litery A i B na zmianę, tzn ciąg liter A, potem ciąg liter B, itd czyli tak jak powinno być.
Użyj tutaj typu Void, a nie Object przy parametryzowaniu SwingWorker.
Chciałem zsynchronizować te dwa wątki żeby litery pojawiały się na zmianę. Niestety, pojawiają się losowo niekoniecznie w tej kolejności, co gorsza same przerywają po chwili...
Dodano:
Jak dopisałem notifyAll() po opublikowaniu litery, to wywala zawsze jedno AB, jednak nie zapętla się
Poprzerabiałem trochę, dodałem bloki synchronized. Wątki są już teoretycznie zsynchronizowane (w konsoli wywołują się odpowiednio), jednak do komponentu dalej litery wpadają losowo. Gdzie jeszcze powinienem to synchronizować? A może w ogóle zmienić podejście?
Metoda process(List<V>) jest wywoływana w EDT, więc tam nie musisz nic synchronizować. Zachowanie jest zgodne z oczekiwanym, gdyż wyniki metody publish są łączone w listę, jeżeli EDT jest zajęty i nie można wywołać process od razu.
W twoim przypadku chyba jednak dobrze by było zmienić podejście. Do głowy przyszedł mi następujący pomysł:
Utworzyć normalne Thready, które generują litery.
W środku tych Threadów tworzyć nowe Runnable dla każdej litery i wywoływać SwingUtilites.invokeLater() na nich.
Rozwiązanie to ma jednak pewną zasadniczą wadę. Jeżeli skolejkujesz za dużo zadań to interfejs się zawiesi. Nowe eventy lądują chyba na końcu listy, więc dowolny event, np kliknięcie, będzie musiał poczekać, aż wykonają się wszystkie zadania, które były w kolejce, gdy był do niej dodawany.
EDIT:
W sumie można skorzystać też z invokeAndWait() i wtedy raczej nie zaspamujemy tak bardzo kolejki do EDT.