JavaRush /Blog Java /Random-PL /Nie można zepsuć Javy wątkiem: Część II - synchronizacja
Viacheslav
Poziom 3

Nie można zepsuć Javy wątkiem: Część II - synchronizacja

Opublikowano w grupie Random-PL

Wstęp

Wiemy więc, że w Javie istnieją wątki, o czym przeczytasz w recenzji „ Jvy wątkiem nie zepsujesz: część I – wątki ”. Wątki są potrzebne do jednoczesnej pracy. Dlatego jest bardzo prawdopodobne, że wątki będą w jakiś sposób ze sobą współdziałać. Rozumiemy, jak to się dzieje i jakie mamy podstawowe kontrole. Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 1

Dawać

Metoda Thread.yield() jest tajemnicza i rzadko używana. W Internecie istnieje wiele odmian jego opisu. Do tego stopnia, że ​​niektórzy piszą o jakiejś kolejce wątków, w której wątek będzie przesuwał się w dół uwzględniając ich priorytety. Ktoś pisze, że wątek zmieni swój status z uruchomionego na uruchamialny (choć nie ma podziału na te statusy, a Java ich nie rozróżnia). Ale w rzeczywistości wszystko jest znacznie bardziej nieznane i w pewnym sensie prostsze. Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 2W temacie dokumentacji metod yieldwystępuje błąd „ JDK-6416721: (wątek specyfikacji) Napraw Thread.yield() javadoc ”. Jeśli to przeczytasz, stanie się jasne, że w rzeczywistości metoda ta yieldprzekazuje jedynie pewne zalecenie planiście wątków Java, że ​​temu wątkowi można przypisać krótszy czas wykonania. Ale to, co faktycznie się stanie, czy program planujący usłyszy zalecenie i co ogólnie zrobi, zależy od implementacji JVM i systemu operacyjnego. A może z jakichś innych czynników. Całe zamieszanie wynikało najprawdopodobniej z ponownego przemyślenia wielowątkowości podczas rozwoju języka Java. Więcej możesz przeczytać w recenzji „ Krótkie wprowadzenie do Java Thread.yield() ”.

Sen - wątek dotyczący zasypiania

Wątek może w trakcie wykonywania zapaść w stan uśpienia. To najprostszy rodzaj interakcji z innymi wątkami. System operacyjny, w którym zainstalowana jest wirtualna maszyna Java, w której wykonywany jest kod Java, ma własny harmonogram wątków, zwany Harmonogramem wątków. To on decyduje, który wątek i kiedy uruchomić. Programista nie może wchodzić w interakcję z tym harmonogramem bezpośrednio z poziomu kodu Java, ale może za pośrednictwem JVM poprosić program planujący o wstrzymanie na chwilę wątku, aby go „uśpić”. Więcej możesz przeczytać w artykułach „ Thread.sleep() ” i „ Jak działa wielowątkowość ”. Ponadto możesz dowiedzieć się, jak działają wątki w systemie operacyjnym Windows: „ Wewnętrzne elementy wątku Windows ”. Teraz zobaczymy to na własne oczy. Zapiszmy następujący kod do pliku HelloWorldApp.java:
class HelloWorldApp {
    public static void main(String []args) {
        Runnable task = () -> {
            try {
                int secToWait = 1000 * 60;
                Thread.currentThread().sleep(secToWait);
                System.out.println("Waked up");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}
Jak widać mamy zadanie, które czeka 60 sekund, po czym program się kończy. Kompilujemy javac HelloWorldApp.javai uruchamiamy java HelloWorldApp. Lepiej uruchomić w osobnym oknie. Na przykład w systemie Windows wyglądałoby to tak: start java HelloWorldApp. Za pomocą polecenia jps poznajemy PID procesu i otwieramy listę wątków za pomocą jvisualvm --openpid pidПроцесса: Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 3Jak widać nasz wątek przeszedł w stan Uśpiony. Tak naprawdę uśpienie bieżącego wątku można wykonać piękniej:
try {
	TimeUnit.SECONDS.sleep(60);
	System.out.println("Waked up");
} catch (InterruptedException e) {
	e.printStackTrace();
}
Pewnie zauważyłeś, że przetwarzamy wszędzie InterruptedException? Rozumiemy dlaczego.

Przerywanie wątku lub Thread.interrupt

Rzecz w tym, że gdy wątek czeka we śnie, ktoś może chcieć przerwać to oczekiwanie. W tym przypadku obsługujemy taki wyjątek. Dokonano tego po Thread.stopuznaniu metody za przestarzałą, tj. przestarzałe i niepożądane w użyciu. Powodem tego było to, że po wywołaniu metody stopwątek został po prostu „zabity”, co było bardzo nieprzewidywalne. Nie mogliśmy wiedzieć, kiedy przepływ zostanie zatrzymany, nie mogliśmy zagwarantować spójności danych. Wyobraź sobie, że zapisujesz dane do pliku, a następnie strumień zostaje zniszczony. Dlatego uznali, że logiczniej będzie nie przerywać przepływu, a poinformować go, że należy go przerwać. To, jak na to zareaguje, zależy od samego przepływu. Więcej szczegółów można znaleźć w artykule Oracle „ Dlaczego Thread.stop jest przestarzały? ” Spójrzmy na przykład:
public static void main(String []args) {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(60);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
W tym przykładzie nie będziemy czekać 60 sekund, ale natychmiast wydrukujemy „Przerwano”. Dzieje się tak, ponieważ nazwaliśmy metodę wątku interrupt. Ta metoda ustawia „wewnętrzną flagę zwaną statusem przerwania”. Oznacza to, że każdy wątek ma wewnętrzną flagę, która nie jest bezpośrednio dostępna. Mamy jednak natywne metody interakcji z tą flagą. Ale to nie jedyny sposób. Wątek może być w trakcie wykonywania, nie czekać na coś, ale po prostu wykonywać działania. Może jednak zapewnić, że będą chcieli go ukończyć w pewnym momencie swojej pracy. Na przykład:
public static void main(String []args) {
	Runnable task = () -> {
		while(!Thread.currentThread().isInterrupted()) {
			//Do some work
		}
		System.out.println("Finished");
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.interrupt();
}
W powyższym przykładzie widać, że pętla whilebędzie działać, dopóki wątek nie zostanie przerwany zewnętrznie. Ważną rzeczą, którą należy wiedzieć o fladze isInterrupted , jest to, że jeśli ją złapiemy InterruptedException, flaga isInterruptedzostanie zresetowana, a następnie isInterruptedzwróci wartość false. W klasie Thread istnieje również metoda statyczna, która ma zastosowanie tylko do bieżącego wątku - Thread.interrupted() , ale ta metoda resetuje flagę na wartość false! Więcej możesz przeczytać w rozdziale „ Przerwanie wątku ”.

Dołącz — oczekiwanie na zakończenie kolejnego wątku

Najprostszym typem oczekiwania jest oczekiwanie na zakończenie innego wątku.
public static void main(String []args) throws InterruptedException {
	Runnable task = () -> {
		try {
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			System.out.println("Interrupted");
		}
	};
	Thread thread = new Thread(task);
	thread.start();
	thread.join();
	System.out.println("Finished");
}
W tym przykładzie nowy wątek będzie uśpiony przez 5 sekund. Jednocześnie wątek główny poczeka, aż wątek uśpiony się obudzi i zakończy swoją pracę. Jeśli przejrzysz JVisualVM, stan wątku będzie wyglądał następująco: Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 4Dzięki narzędziom monitorującym możesz zobaczyć, co dzieje się z wątkiem. Metoda joinjest dość prosta, ponieważ jest to po prostu metoda z kodem Java, która jest wykonywana wait, gdy wątek, w którym jest wywoływana, jest aktywny. Gdy wątek umrze (w momencie zakończenia), oczekiwanie zostaje zakończone. Na tym polega cała magia tej metody join. Przejdźmy zatem do najciekawszej części.

Monitor koncepcji

W wielowątkowości istnieje coś takiego jak Monitor. Ogólnie rzecz biorąc, słowo monitor jest tłumaczone z łaciny jako „nadzorca” lub „nadzorca”. W ramach tego artykułu postaramy się przypomnieć istotę, a chętnych proszę o szczegółowe zapoznanie się z materiałem z linków. Naszą podróż zacznijmy od specyfikacji języka Java, czyli od JLS: „ 17.1. Synchronizacja ”. Mówi co następuje: Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 5Okazuje się, że w celu synchronizacji pomiędzy wątkami Java wykorzystuje pewien mechanizm zwany „Monitorem”. Z każdym obiektem jest powiązany monitor, a wątki mogą go blokować lub odblokowywać. Następnie na stronie Oracle znajdziemy samouczek szkoleniowy: „ Intrinsic Locks and Synchronization ”. W tym samouczku wyjaśniono, że synchronizacja w języku Java opiera się na wewnętrznej jednostce zwanej blokadą wewnętrzną lub blokadą monitora. Często taki zamek nazywany jest po prostu „monitorem”. Widzimy również ponownie, że z każdym obiektem w Javie jest powiązana wewnętrzna blokada. Możesz przeczytać „ Java – wewnętrzne blokady i synchronizacja ”. Następnie ważne jest, aby zrozumieć, w jaki sposób obiekt w Javie może zostać powiązany z monitorem. Każdy obiekt w Javie posiada nagłówek – rodzaj wewnętrznych metadanych, które nie są dostępne programiście z poziomu kodu, a których maszyna wirtualna potrzebuje, aby poprawnie pracować z obiektami. Nagłówek obiektu zawiera MarkWord, który wygląda następująco: Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 6

https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf

Bardzo przydatny jest tutaj artykuł Habr: „ Ale jak działa wielowątkowość? Część I: synchronizacja ”. Do tego artykułu warto dodać opis z Podsumowanie bloku zadań z bugtakera JDK: „ JDK-8183909 ”. To samo możesz przeczytać w „ JEP-8183909 ”. Tak więc w Javie monitor jest powiązany z obiektem, a wątek może zablokować ten wątek lub powiedzieć „uzyskaj blokadę”. Najprostszy przykład:
public class HelloWorld{
    public static void main(String []args){
        Object object = new Object();
        synchronized(object) {
            System.out.println("Hello World");
        }
    }
}
Tak więc, używając słowa kluczowego, synchronizedbieżący wątek (w którym wykonywane są te linie kodu) próbuje użyć monitora powiązanego z obiektem objecti „uzyskać blokadę” lub „przechwycić monitor” (druga opcja jest nawet lepsza). Jeśli nie ma rywalizacji o monitor (tj. nikt inny nie chce synchronizować tego samego obiektu), Java może spróbować przeprowadzić optymalizację zwaną „blokowaniem stronniczym”. Tytuł obiektu w programie Mark Word będzie zawierał odpowiedni znacznik i zapis wątku, do którego podłączony jest monitor. Zmniejsza to obciążenie podczas przechwytywania obrazu z monitora. Jeśli monitor był już wcześniej przywiązany do innego wątku, to zablokowanie nie wystarczy. JVM przełącza się na następny typ blokowania - blokowanie podstawowe. Wykorzystuje operacje porównania i zamiany (CAS). Jednocześnie nagłówek w Mark Word nie przechowuje już samego Mark Word, ale link do jego przechowywania + tag zostaje zmieniony, tak aby JVM zrozumiała, że ​​używamy podstawowego blokowania. Jeśli istnieje rywalizacja o monitor kilku wątków (jeden przechwycił monitor, a drugi czeka na zwolnienie monitora), wówczas znacznik w Mark Word zmienia się i Mark Word zaczyna zapisywać odniesienie do monitora jako obiekt - jakiś wewnętrzny byt JVM. Jak stwierdzono w JEP, w tym przypadku wymagana jest przestrzeń w obszarze pamięci Native Heap do przechowywania tej jednostki. Link do miejsca przechowywania tej jednostki wewnętrznej będzie znajdować się w obiekcie Mark Word. Zatem, jak widzimy, monitor jest tak naprawdę mechanizmem zapewniającym synchronizację dostępu wielu wątków do współdzielonych zasobów. Istnieje kilka implementacji tego mechanizmu, pomiędzy którymi przełącza się maszyna JVM. Dlatego dla uproszczenia, mówiąc o monitorze, tak naprawdę mówimy o zamkach. Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 7

Zsynchronizowane i oczekujące przy zamku

Pojęcie monitora, jak widzieliśmy wcześniej, jest ściśle powiązane z koncepcją „bloku synchronizacyjnego” (lub, jak to się nazywa, sekcji krytycznej). Spójrzmy na przykład:
public static void main(String[] args) throws InterruptedException {
	Object lock = new Object();

	Runnable task = () -> {
		synchronized (lock) {
			System.out.println("thread");
		}
	};

	Thread th1 = new Thread(task);
	th1.start();
	synchronized (lock) {
		for (int i = 0; i < 8; i++) {
			Thread.currentThread().sleep(1000);
			System.out.print("  " + i);
		}
		System.out.println(" ...");
	}
}
Tutaj główny wątek najpierw wysyła zadanie do nowego wątku, a następnie natychmiast „przechwytuje” zamek i wykonuje z nim długą operację (8 sekund). Przez cały ten czas zadanie nie może wejść do bloku w celu jego wykonania synchronized, ponieważ zamek jest już zajęty. Jeśli wątek nie może uzyskać blokady, będzie na nią czekać przy monitorze. Gdy tylko go otrzyma, będzie kontynuował wykonywanie. Kiedy wątek opuszcza monitor, zwalnia blokadę. W JVisualVM wyglądałoby to tak: Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 8Jak widać, status w JVisualVM nazywa się „Monitor”, ponieważ wątek jest zablokowany i nie może zajmować monitora. Stan wątku można też poznać w kodzie, lecz nazwa tego stanu nie pokrywa się z terminami JVisualVM, chociaż są one podobne. W takim przypadku th1.getState()pętla forzwróci BLOCKED , ponieważ Kiedy pętla jest uruchomiona, monitor lockjest zajęty mainprzez wątek, a wątek th1jest zablokowany i nie może kontynuować pracy do czasu przywrócenia blokady. Oprócz bloków synchronizacji można synchronizować całą metodę. Przykładowo metoda z klasy HashTable:
public synchronized int size() {
	return count;
}
W jednej jednostce czasu metoda ta zostanie wykonana tylko przez jeden wątek. Ale potrzebujemy zamka, prawda? Tak, potrzebuję tego. W przypadku metod obiektowych blokada będzie miała postać this. Istnieje interesująca dyskusja na ten temat: „ Czy stosowanie metody synchronicznej zamiast bloku synchronicznego ma przewagę? ”. Jeśli metoda jest statyczna, to blokada nie będzie this(ponieważ w przypadku metody statycznej nie może być this), ale obiekt klasy (na przykład Integer.class).

Czekaj i czekaj na monitorze. Metody notify i notifyAll

Wątek ma inną metodę oczekiwania, która jest podłączona do monitora. W przeciwieństwie do sleepi joinnie można go po prostu nazwać. A jego imię to wait. Metoda wykonywana jest waitna obiekcie, na którego monitorze chcemy poczekać. Zobaczmy przykład:
public static void main(String []args) throws InterruptedException {
	    Object lock = new Object();
	    // task будет ждать, пока его не оповестят через lock
	    Runnable task = () -> {
	        synchronized(lock) {
	            try {
	                lock.wait();
	            } catch(InterruptedException e) {
	                System.out.println("interrupted");
	            }
	        }
	        // После оповещения нас мы будем ждать, пока сможем взять лок
	        System.out.println("thread");
	    };
	    Thread taskThread = new Thread(task);
	    taskThread.start();
        // Ждём и после этого забираем себе лок, оповещаем и отдаём лок
	    Thread.currentThread().sleep(3000);
	    System.out.println("main");
	    synchronized(lock) {
	        lock.notify();
	    }
}
W JVisualVM będzie to wyglądać tak: Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 10Aby zrozumieć jak to działa, należy pamiętać, że metody waitodnoszą się notifydo java.lang.Object. Wydaje się dziwne, że metody związane z wątkami znajdują się w pliku Object. Ale tutaj kryje się odpowiedź. Jak pamiętamy, każdy obiekt w Javie ma nagłówek. Nagłówek zawiera różne informacje serwisowe, w tym informacje o monitorze — dane o stanie zablokowania. Jak pamiętamy, każdy obiekt (tj. każda instancja) ma powiązanie z wewnętrzną jednostką JVM zwaną blokadą wewnętrzną, zwaną także monitorem. W powyższym przykładzie zadanie opisuje, że wprowadzamy blok synchronizacji na monitorze powiązanym z lock. Jeśli możliwe jest uzyskanie blokady na tym monitorze, to wait. Wątek wykonujący to zadanie zwolni monitor lock, ale dołączy do kolejki wątków oczekujących na powiadomienie na monitorze lock. Ta kolejka wątków nazywa się WAIT-SET, co lepiej oddaje istotę. To raczej zestaw niż kolejka. Wątek maintworzy nowy wątek z zadaniem task, uruchamia je i czeka 3 sekundy. Dzięki temu z dużym prawdopodobieństwem nowy wątek przechwyci blokadę przed wątkiem maini ustawi się w kolejce na monitorze. Po czym sam wątek mainwchodzi do bloku synchronizacji locki wykonuje powiadomienie wątku na monitorze. Po wysłaniu powiadomienia wątek mainzwalnia monitor lock, a nowy wątek (który wcześniej czekał) lockkontynuuje wykonywanie po oczekiwaniu na zwolnienie monitora. Możliwe jest wysłanie powiadomienia tylko do jednego z wątków ( notify) lub do wszystkich wątków w kolejce na raz ( notifyAll). Więcej informacji można znaleźć w artykule „ Różnica między notify() i notifyAll() w Javie ”. Należy pamiętać, że kolejność powiadomień zależy od implementacji JVM. Więcej informacji można znaleźć w artykule „ Jak rozwiązać problem głodu za pomocą funkcji powiadamiania i powiadamiania wszystkich? ”. Synchronizację można przeprowadzić bez określania obiektu. Można to zrobić, gdy synchronizowana jest nie oddzielna sekcja kodu, ale cała metoda. Na przykład dla metod statycznych blokadą będzie obiekt klasy (uzyskany za pomocą .class):
public static synchronized void printA() {
	System.out.println("A");
}
public static void printB() {
	synchronized(HelloWorld.class) {
		System.out.println("B");
	}
}
Jeśli chodzi o użycie zamków, obie metody są takie same. Jeżeli metoda nie jest statyczna to synchronizacja zostanie przeprowadzona według aktualnego instance, czyli według this. Nawiasem mówiąc, wcześniej powiedzieliśmy, że za pomocą tej metody getStatemożna uzyskać status wątku. Oto wątek umieszczony w kolejce przez monitor, jego status to WAITING lub TIMED_WAITING, jeśli metoda waitokreśliła limit czasu oczekiwania. Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 11

Cykl życia wątku

Jak widzieliśmy, przepływ zmienia swój status w trakcie życia. W istocie zmiany te stanowią cykl życia wątku. Gdy wątek jest właśnie utworzony, ma status NOWY. W tej pozycji jeszcze się nie uruchomił, a Harmonogram wątków Java nie wie jeszcze nic o nowym wątku. Aby planista wątków wiedział o wątku, musisz wywołać funkcję thread.start(). Następnie wątek przejdzie w stan RUNNABLE. W Internecie istnieje wiele błędnych schematów, w których stany Runnable i Running są rozdzielone. Ale to błąd, bo... Java nie rozróżnia stanów „gotowy do uruchomienia” i „uruchomiony”. Kiedy wątek żyje, ale nie jest aktywny (nie można go uruchomić), znajduje się w jednym z dwóch stanów:
  • ZABLOKOWANY - oczekuje na wejście do strefy chronionej, tj. do synchonizedbloku.
  • WAITING — czeka na inny wątek w oparciu o warunek. Jeśli warunek jest spełniony, planista wątku uruchamia wątek.
Jeśli wątek oczekuje według czasu, jest w stanie TIMED_WAITING. Jeśli wątek nie jest już uruchomiony (ukończony pomyślnie lub z wyjątkiem), przechodzi w stan ZAKOŃCZONY. Aby poznać stan wątku (jego stan) stosuje się metodę getState. Wątki mają również metodę isAlive, która zwraca wartość true, jeśli wątek nie jest zakończony.

LockSupport i parkowanie gwintów

Od wersji Java 1.6 istniał ciekawy mechanizm o nazwie LockSupport . Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 12Ta klasa kojarzy „zezwolenie” lub pozwolenie z każdym wątkiem, który z niej korzysta. Wywołanie metody parkzwraca się natychmiast, jeśli dostępne jest zezwolenie, zajmując to samo zezwolenie podczas wywołania. W przeciwnym razie jest zablokowany. Wywołanie metody unparkudostępnia zezwolenie, jeśli jeszcze nie jest dostępne. Jest tylko jedno zezwolenie. W interfejsie API Java LockSupportokreślony plik Semaphore. Spójrzmy na prosty przykład:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(0);
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            // Просим разрешение и ждём, пока не получим его
            e.printStackTrace();
        }
        System.out.println("Hello, World!");
    }
}
Ten kod będzie czekał wiecznie, ponieważ semafor ma teraz 0 zezwoleń. A kiedy zostanie wywołany w kodzie acquire(tj. poprosi o pozwolenie), wątek czeka, aż otrzyma pozwolenie. Ponieważ czekamy, jesteśmy zobowiązani to przetworzyć InterruptedException. Co ciekawe, semafor implementuje oddzielny stan wątku. Jeśli spojrzymy na JVisualVM, zobaczymy, że naszym stanem nie jest Czekanie, ale Park. Nie możesz zepsuć Javy wątkiem: Część II - synchronizacja - 13Spójrzmy na inny przykład:
public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            //Запаркуем текущий поток
            System.err.println("Will be Parked");
            LockSupport.park();
            // Как только нас распаркуют - начнём действовать
            System.err.println("Unparked");
        };
        Thread th = new Thread(task);
        th.start();
        Thread.currentThread().sleep(2000);
        System.err.println("Thread state: " + th.getState());

        LockSupport.unpark(th);
        Thread.currentThread().sleep(2000);
}
Status wątku będzie WAITING, ale JVisualVM rozróżnia waitfrom synchronizedi parkfrom LockSupport. Dlaczego ten jest tak ważny LockSupport? Wróćmy ponownie do API Java i spójrzmy na stan wątku WAITING . Jak widać, można się do niego dostać tylko na trzy sposoby. 2 sposoby - to waiti join. A trzeci to LockSupport. Blokady w Javie są zbudowane na tych samych zasadach LockSupporti reprezentują narzędzia wyższego poziomu. Spróbujmy użyć jednego. Spójrzmy na przykład na ReentrantLock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{

    public static void main(String []args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable task = () -> {
            lock.lock();
            System.out.println("Thread");
            lock.unlock();
        };
        lock.lock();

        Thread th = new Thread(task);
        th.start();
        System.out.println("main");
        Thread.currentThread().sleep(2000);
        lock.unlock();
    }
}
Podobnie jak w poprzednich przykładach, tutaj wszystko jest proste. lockczeka, aż ktoś zwolni zasób. Jeśli zajrzymy do JVisualVM, zobaczymy, że nowy wątek zostanie zaparkowany, dopóki mainwątek nie zablokuje go. Więcej o blokadach przeczytasz tutaj: „ Programowanie wielowątkowe w Javie 8. Część druga. Synchronizacja dostępu do obiektów zmiennych ” oraz „ API Java Lock. Teoria i przykład użycia ”. Aby lepiej zrozumieć implementację blokad, warto przeczytać o Phazerze w przeglądzie „ Klasa Phaser ”. A mówiąc o różnych synchronizatorach, musisz przeczytać artykuł na temat Habré „ Java.util.concurrent.* Synchronizers Reference ”.

Całkowity

W tej recenzji przyjrzeliśmy się głównym sposobom interakcji wątków w Javie. Dodatkowy materiał: #Wiaczesław
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION