Techniki Zaawansowane
8 godz. 35 min · Java · Full-stack i Programowanie
Rafał SolarskiW poczatkowych lekcjach przyjrzymy sie pokladowi, na jakim bedziemy uruchamiali nasze programy. Wykorzystamy VisualVM do podgladania parametrów JVM. Zobaczymy równiez jakie zasoby sa tam dostepne oraz dowiesz sie jak monitorowac to, czy dobrze z nich korzystamy. Opowiemy Ci tez miedzy innymi o tym, jak zrobic heap i thread dump, oraz jak wlaczyc logi GC.
Kolejne lekcje kursu zostaly poswiecone wyrazeniom regularnym. Wyrazenia regularne mozemy wykorzystac nie tylko do walidacji danych wprowadzania przez uzytkownika, ale równiez do dzielenia tekstu oraz wyluskiwania wystapien wzorców w duzym tekscie. W tym rozdziale dowiemy sie miedzy innymi tego, jak mozemy wykorzystac ten potencjal z poziomu Javy.
Dzieki typom generycznym jestesmy w stanie osiagnac bezpieczenstwo typów w trakcie kompilacji przy zachowaniu elastycznosci pisanego kodu. W nastepnym rozdziale kursu przyjrzymy sie temu, jak mozemy je wykorzystac, oraz w jaki sposób czasami nalezy z tego bezpieczenstwa zrezygnowac.
Interfejsy funkcyjne to sposób na przeniesienie odrobiny swiata programowania funkcyjnego do Javy. Gdy polaczymy je z wyrazeniami lambda oraz API strumieni pozwola nam pisac te sama logike w duzo bardziej przejrzysty sposób.
Kolekcje, czyli listy, mapy, zbiory i kolejki to jedne z najczesciej wykorzystywanych klas w codziennej pracy. To jak dobrze poznamy ich mozliwosci ma ogromny wplyw na to, w jaki sposób bedziemy podchodzili do rozwiazywania wyzwan na naszej drodze.
System plików to zasób bez którego ciezko sobie poradzic. Pozwalaja nam na dostarczanie konfiguracji, danych wejsciowych i wyjsciowych, a przez to równiez integrowanie ze soba calych systemów.W tym rozdziale nauczymy sie korzystac z tych dobrodziejstw w bezpieczny sposób wykorzystujac m.in. IO Streams.
Trzymanie daty w String to niekoniecznie najwygodniejszy sposób. Juz od pewnego czasu Java dysponuje bardzo wygodnym Date&Time API - zobaczymy, co mozemy w nim znalezc.
Wielowatkowosc nie jest prosta... i dlugo tak jeszcze pozostanie. Nawet jesli korzystamy z frameworków, które próbuja ja przed nami ukryc, to ciagle musimy byc swiadomi problemów jakie sie z nia wiaza. W tym rozdziale poznamy glówne problemy na jakie mozna natrafic programujac wielowatkowo w Javie. Poznamy równiez klasy, które zdecydowanie pomoga nam zapanowac nad ta zlozonoscia.
JDBC to najbardziej podstawowy sposób laczenia sie z baza SQL z poziomu Javy. W wielu systemach/aplikacjach stosuje sie rozwiazania ORM takie jak JPA/Hibernate. Mimo wszystko warto wiedziec jak to pod spodem dziala oraz umiec poradzic sobie w aplikacjach, gdzie wybrano bardziej "lekkie" podejscie niz Hibernate.
Na koniec kursu wykorzystamy zdobyta wiedze, aby stworzyc prosta aplikacje pozwalajaca na przechowywanie danych o wydatkach. Po stworzeniu coreu aplikacji, mozesz spróbowac uzupelnic go za pomoca GUI.
Ten kurs stworzony zostal przede wszystkim z mysla o osobach, które juz poznaly podstawy jezyka, takie jak zmienne, mechanizmy kontroli wykonania, klasy, typy generyczne.Jezeli chcesz poczuc sie swobodnie nie tylko jesli chodzi o mechanizmy jezyka, ale równiez pod wzgledem znajomosci standardowej biblioteki Javy - to kurs w sam raz dla Ciebie. Materialy beda przydatne dla studentów, którzy znaja juz podstawowa skladnie Javy; programistów Javy chcacych poszerzyc lub uporzadkowac swoja wiedze; programistów innych jezyków, którzy chca poznac inny stack technologiczny.
Cześć w tej lekcji przyjrzymy się kolejnemu ciekawemu
problemowi związanemu z wielowątkowością w javie jakim jest race
conditions okej otwórzmy sobie kod tutaj mam przygotowany taki
program króciutki i w tym programie sobie tworzymy jakiś
thread pool 8 wątkowy tak jedziemy sobie w obrotach
jakiejś pętli w jakimś forze tak i wywołujemy tutaj execute jakąś
metodę tak i ta metoda inkrementuje nam jakiś
straszny współdzielony state jakim jest jakiś integer okej
zobaczmy jakie są problemy z tym kodem na razie go sobie uruchommy jak widzimy
po wyprintowaniu wartości tego countera dostajemy trochę inną wartość niż
liczba obrotów pętli powinniśmy dostać dokładnie taką samą wartość i teraz
przyjrzyjmy się temu state'owi co tu się dzieje widzimy że
to jest private static int i teraz oglądając poprzednią lekcję
powinieneś wiedzieć że tutaj powinniśmy dodać przynajmniej volatile albo jakoś zsynchronizować
zapis i odczyt do tego czegoś żeby przekraczać
w ogóle to memory barrier tak w tym miejscu każdy z tych threadów może
mieć coś innego tak i teraz co nam nawet idea podpowiada
w tym momencie że tutaj ta
inkrementacja jak też każda inna na wartości w javie tak
jakby jest dwuetapowa tak nie jest atomowa pod tym względem najpierw ona odczytuje
wartość możemy to sobie rozbić counter to jest samo co counter
plus 1 prawda no i teraz tu mamy najpierw
read tak odczytujemy sobie wartość później do tej
wartości dodajemy plus 1 i dopiero zapisujemy no i to by było wszystko
fajnie gdyby nie to że tutaj pomiędzy to może
już jeden tu odczytujemy jakąś wartość przykładowo 5 w
międzyczasie jakiś wątek zdążył ustawić tutaj już 6 tak tam
powiedzmy 7 no i w tym momencie my ustawiamy nasze
6 mieliśmy 5 daliśmy 1 ustawiliśmy 6 było tutaj 7 tak
no to jest ten problem że mamy niezsynchronizowany dostęp
do współdzielonego stanu tak kolejny raz
to że my współdzielimy jakieś zmienne jest problemem tak okej
teraz znowu jak w poprzedniej lekcji zaprezentuje ci jak na kilka sposobów jesteśmy sobie w
stanie poradzić z tym problemem pierwsze najprostsze podejście to jest wykorzystanie
synchronized w tym miejscu jeżeli mamy synchronized to
mamy załatwione to że powiedzmy jeden wątek może wejść
do tego do tej metody tak to jeszcze dochodzi jeden
szczegół o którym zaraz powiem ale w każdym razie dopóki ten wątek nie
wyjdzie z tej metody to drugi nie wejdzie prawda drugą
rzeczą jest to że jeżeli tutaj mamy dostęp do jakiejś zmiennej tak współdzielonej
to dzięki temu że mamy synchronized z poprzedniej lekcji wiemy że mamy załatwiony
dostęp przekraczający memory barrier w tym momencie prawda z tego
względu też mogłem tutaj usunąć volitile z tego miejsca jak to sobie uruchomimy
w tym momencie to powinno nam zadziałać powinniśmy dostać po prostu tę liczbę jak
widzimy wynik się zgadza okej dobra teraz z takim szczegółem
a odnośnie synchronized to synchronize zakłada tak
zwany monitor jest to pewnego rodzaju lock na
pewnym obiekcie jak tworzymy sobie jakikolwiek obiekt w javie tak
to razem z nim mamy tak zwany właśnie monitor i na tym
monitorze możemy zakładać lock tutaj tu się wszystko dzieje niejawnie
zasadniczo ten synchronoise możemy sobie rozpisać w inny sposób który
bardziej unaoczni co tu się dzieje taki synchronize możemy przepisać w ten sposób
to jest synchronized na pewnym obiekcie
w tym momencie jest to main kropka class tak czyli obiekt
klasy tej klasy w której jesteśmy tak tym momencie jak tutaj wchodzimy
dochodzimy do tego miejsca jest nakładany log na tym
obiekcie wykorzystując monitor tego obiektu tak i dopóki inny
obiekt wchodzi do tej samej metody i on znowu ma lock
na tej powiedzmy metodzie tak który tutaj powiedzmy on żeby
założyć lock musi sprawdzić czy tutaj jakiś poprzedni nie założył też locka to
w tym momencie on nie wejdzie dopóki ten poprzedni wątek numer jeden stąd
nie wyjdzie tak okej i teraz powiedzmy taka sytuacja my
mamy w tej w tym momencie metodą statyczną tak dlatego jeżeli
robimy synchronized tutaj w tym miejscu tak to
jest to samo jak byśmy zrobili właśnie synchronized na obiekcie klasy
za to kiedy jesteśmy w kontekście jakiegoś obiektu
tak nie metoda statyczna tylko metoda instancji jakiejś to
jeżeli tutaj zrobimy synchronized to jest to samo jak byśmy zrobili
tutaj wewnątrz synchronized na tym obiekcie czyli gdybyśmy
tutaj zrobi na this tak dokładnie to samo okej kolejnym
sposobem rozwiązania problemu jest wykorzystanie wrappera jakim jest atomic
integer teraz na atomic integer możemy spokojnie robić increment
and get w tym miejscu tak on sprawdzi on tutaj wykorzystuje powiedzmy taką niskopoziomową
operacje procesora która nam po prostu zainkrementuje tę wartość
tak teraz jak to odpalimy to dostaniemy właściwy wynik prawda
tutaj też przypominam że to co tutaj wrzucimy wewnątrz tak tego
atomic integer no to w tym momencie jest volatile
tutaj prawda jest storage'owane w zmiennej która jest volatile
okej kolejną rzeczą jest to że jeżeli tutaj mamy atomicinteger
to on nie musi być już tutaj volatile
tak z tego względu że on już istnieje jest ustawiony w
momencie kiedy tworzyliśmy tutaj robiliśmy execute prawda czyli
kiedy były tworzone thready to jeszcze warto dodawać zawsze final tak
to już w ogóle będzie super okej to były takie sposoby właśnie implementacji takiego
prostego countera tak który jest jakąś zmienną współdzieloną tutaj
kolejny raz podkreślam główną taką przyczyną problemu z visibility
problem tak z tym memory barrier czy właśnie
conditions często jest to że my mamy jakieś współdzielony mutowalny
state no i to jest powiedzmy ten problem czasami jesteśmy w stanie tak
pisać nasz kod aby on był wykorzystywał obiekty które nie są mutowalne albo
przynajmniej liczba mutowalnych obiektów była ograniczona
powiedzmy do jednego wątku tak czyli powiedzmy tylko w jednym wątku modyfikujemy
tutaj akurat jeżeli my implementuje taki counter no ten counter
jest stosunkowo prosty do zaimplementowania i powiedzmy tutaj jest jeżeli
znamy podstawy tak to jest tak mało miejsc których jesteśmy
w stanie zapomnieć prawda jeżeli implementujemy coś takiego prostego jak
counter na przykład jakieś metryki ile razy wysłaliśmy w ciągu
życia aplikacji jakiegoś maila i tak dalej to możemy oddelegować
to do jakiegoś zewnętrznego narzędzia tak czyli możemy to wysyłać do jakiegoś influx'a
takich innych bazach które tu storage'ujemy nie musimy
tego trzymać w naszej aplikacji a po drugie też jeżeli
zrobimy to w jakimś atomicintegerze tak no to jesteśmy sobie w
stanie nawet na to pozwolić tak jeżeli tego nie ma za dużo też
z tego z powiedzmy tu jest niewiele miejsc gdzie jesteśmy w stanie się potknąć o
ile jesteśmy świadomi tych zagrożeń tutaj jeszcze chciałbym
się wrócić do samego synchronized do tych wykorzystanie synchronized
jest w wielu wypadkach powiedzmy
najbardziej takie uniwersalne z tego względu że robiąc
synchronized jesteśmy w stanie zapewnić że do danej metody wchodzi jeden
wątek tak czyli tutaj możemy zamykać w takiej metodzie naszą sekcje krytyczną
czyli miejsce kodu w którym powiedzmy może
wbijać się kilka wątków na raz tak no to wtedy możemy właśnie wykorzystując synchronized
czy tak czy czy powiedzmy w tym wersji z blokiem jesteśmy
w stanie to ograniczać problem z synchronized jest taki że
tutaj nawet jeżeli używamy tego bloku no to nie mamy rozróżnienia
między powiedzmy metodami które robią read write i
też powiedz mi takiej synchronizowanie na synchronize gdzie nie możemy zdefiniować
jakiegoś timeout'u więc jeżeli coś nam tej zawiśnie na czymś takim no to
my powiedzmy aplikacja będzie w tym miejscu czekała po prostu w nieskończoność
o ile coś wcześniej na przykład jakiś timeout nie poleci w jakimś innym miejscu tak tutaj nawet
nie dostaniemy wyjątku z tego względu warto zainteresować
się czymś takim czym jest log tak to jest coś takiego
powiedzmy na czym jesteśmy po pierwsze w stanie zrobić właśnie
jakieś wait'y na przykład trylock
to jest tak jakbyśmy wchodzili do bloku synchronized w tym miejscu tylko tutaj
jeżeli dochodzimy tego miejsca i nasz lock jest zajęty to możemy zdefiniować
jakiś timeout tak i wtedy musimy sprawdzić
jeszcze powiedzmy tą flagę czy udało się powiedzmy czy ten timeout
nie został przebity tak poza tym lock ma jeszcze implementację
reentrantreadwritelock to jest bardzo ciekawa używając
tego obiektu jesteśmy w stanie sprecyzować które
metody zapisują stan tak czyli powiedzmy mają
write a które metody tylko go odczytują i możemy dzięki temu niezależnie
lockować tak jakby write w tym momencie powinien zablokować wszystkie read'y
a read'y może powinny być na przykład nie zależnie nie powinno
się nawzajem blokować prawda więc odsyłam cię właśnie do tej klasy lock niestety
wykorzystanie synchronized tak samo jak locków jak wszelkiego rodzaju
kodu który blokuje nasz kod tak czyli miejsca w których
my czekamy na coś tak tak jakby te timeout'y trochę poprawiają sytuację
ale te sytuacje mogą być tak jakby przyczyną
czegoś co nazywa się z bad lock czyli sytuacja kiedy wątek numer
jeden czeka na wątek numer dwa na przykład jest zalockowany na jakimś
synchornize na jakimś monitorze którą założył a wątek
nr dwa czeka na wątek numer jeden bo on też czeka na jakiś dostęp
tak który wątek numer jeden powiedzmy sobie założył monitor na tym tak o
tym problemie właśnie dead locku dowiemy się w kolejnej
lekcji za te lekcje za ten czas dzięki wielkie
i do usłyszenia