Wpis z mikrobloga

Czy majac watek, ktorego glowna metoda wyglada tak:

@Override public void run(){
running = true;
while(running){

if(infected.size() > 0){ /* loop that reads People from list of Infected, and informs their contacts */
Person p = infected.removeFirst();
ArrayList contacts = database.getContactRecords().get(p);
for(Contact c: contacts) {
Person p2 = c.getPhone();
p2.notifiedAboutPositiveContact();
numberContactsNotified.getAndIncrement();
}
}
pause(100L);
}
}

Czy powinienem uzyc "synchronized(infected)" i dopiero robic to dalej? Czy jak watek zaczal swoja metode run() moze nastapic zmiana kontekstu czy run() zostanie wykonane i dopiero moze zastapic zmiana kontekstu?

#java #programowanie
  • 16
@cugowski: Skąd bierze się linked list infected w wątku?

Jeżeli przypisujesz referencję w konstruktorze:

this.infected = infected
to ten kod nie jest bezpieczny pod względem wielowątkowym ze względu na mutację kolekcji metodą removeFirst()

Możesz oczywiście zrobić blok synchronizacji ale to dosyć zawodne rozwiązanie, w każdym miejscu gdzie modyfikujesz listę musisz o tym pamiętać. Lepszym rozwiązaniem byłoby przekazywać już bezpieczną wątkowo implementację Listy do wątku, możesz to zrobić wrappując ją przy pomocy:
@lazyinit: LinkedList infected to prywatna zmienna klasy(Website). Klasa w konstruktorze inicjalizuje infected uzywajac new LinkedList(). W calej aplikacji jest tylko jeden watek tej klasy. Mam inna klase Person, ktora pozniej za pomoca referencji wywoluje metody Website.

Wszystkie te metody(klasy Website) dzialaja na bezpiecznej watkowo liscie(Collections.snchronizedList(....)). Dodatkowo we wszystkich metodach operujacych na tej liscie blokuje ja uzywajac "synchronized(syncList){}).

Edit: w sumie skoro w konstruktorze tworze bezpieczna liste, to czy w run() moge
@cugowski:

w Swingu

[*]

Przy pracy z nim (jak z większością bibliotek UI) najważniejsze jest żeby
- nie blokować głównego wątku, wszystko co może trwać więcej niż parę ms lepiej jest obsługiwać asynchronicznie. Może to być poprzez wykorzystanie CompletableFuture.runAsync / supplyAsync
- wszystko co tyka UI powinno być wykonywane z głównego wątku (struktury danych dla UI nie są bezpieczne wątkowo np drzewa elementów na widoku) możesz zmienić wątek wykonania poprzez:

SwingUtilities.invokeLater(...)
@lazyinit: To juz tez mam zmienione. Wczesniej(celowo przez prowadzacego) odswiezanie GUI bylo wywolywane z watku Website. Wyrzucilem to i zamiast tego w klasie GUI ustawilem javax.swing.Timer, ktory co jakis czas wywoluje actionPerformed, tam odswiezane sa wszystkie kontrolki. Nie pamietam dokladnie, ale mialem jakis problem z implementacja SwingUtilities.invokeLater(...).
javax.swing.Timer,


@cugowski: Ten kod będzie działał proaktywnie (co jakiś czas odświeżę wszystkie kontrolki...) , UI lepiej żeby działało reaktywnie (zmiana w modelu pociąga odświeżenie widoku, reaguję tylko na zmiany...)

Najważniejsze żeby oddzielić model od widoku, widok powinien być w jakiś sposób informowany o zmianach w modelu. Może to być Observable, EventListenery itp.

Dlaczego SwingUtilities.invokeLater(...) sprawiało problemy?
@lazyinit: Zdecydowanie ma to sens. Finalnie tak zamienie. Nie do konca teraz pamietam, ale chyba chodzilo mi o to, zeby na potrzeby testow i debugowania odswiezac kontrolki co jakies 2-3 sekundy, zebym zauwazyl zmiany i nie moglem znalezc rozwiazania jak to zrobic za pomoca SwingUtilities.invokeLater(...).
@lazyinit: Sorki, juz wiem co mi sprawialo problemy. GUI powinno na biezaco pokazywac stan infected. Website, Person, Population i Database, kazda na swoj sposob mierzy ilosc 'infected', ktore pozniej jest pokazywane w GUI, ktore na biezaco odswieza stan. Czyli, jesli dobrze to rozumiem, w tle caly czas pracuja sobie watki Person, komunikuja sie z Website i Database, wiec SwingUtilities.invokeLater(...) caly czas czeka, az bedzie mozliwosc wykonania? Tak to rozumiem, bo wykonuje
@cugowski: Chmm, domyślam się że Website pobiera dane poprzez HTTP a Database z bazy poprzez JDBC?

Jeżeli tak to obydwa źródła tych danych się są reaktywne tj. klient musi wysłać request HTTP lub klient musi wysłać zapytanie SQL żeby dostać nowe dane.

Najprostsze rozwiązanie to guzik Refresh który pobiera na żądanie te dane.

Twój Website (Model) może mieć metodę refreshInfected() która wysyła GETa i na podstawie rezultatu odświeża swój wewnętrzny stan
@lazyinit: Nie, nie. Website to tylko taki model, nie pobiera zadnych danych, to samo Database. Aplikacja w zden sposob nie komunikuje sie w sposob zewnetrzny ;)

Kurde, szkoda, ze gdzies tego nie poruszylem, zaoszczedzilo by to mnostwo Twojego czasu, ktory poswieciles na ten post. Przepraszam kolego.
@lazyinit: Co mam to mam, na poprawianie tego wszystkiego bede jeszcze pozniej mial czas, takze jesli bedzie spoosbnosc to skorzystam z tego co mi podeslales.
Podziwiam Twoja wiedze w temacie. Programujesz w javie na co dzien?
Dzieki za dzisiejsza pomoc kolego ;)