Samouczki Java mówią, że tworzenie wątku jest kosztowne. Ale dlaczego dokładnie jest to drogie? Co dokładnie dzieje się, gdy tworzony jest wątek Java, który powoduje, że jego tworzenie jest kosztowne? Przyjmuję to stwierdzenie za prawdziwe, ale interesuje mnie tylko mechanika tworzenia wątków w JVM.
Narzut cyklu życia nici. Tworzenie wątków i porzucanie nie są bezpłatne. Rzeczywisty narzut różni się w zależności od platformy, ale tworzenie wątków zajmuje dużo czasu, wprowadzając opóźnienia w przetwarzaniu żądań i wymaga pewnej aktywności przetwarzania przez JVM i system operacyjny. Jeśli żądania są częste i lekkie, jak w większości aplikacji serwerowych, utworzenie nowego wątku dla każdego żądania może zużywać znaczne zasoby komputerowe.
Z Java Concurrency in Practice
Brian Goetz, Tim Peierls, Joshua Bloch, Joseph Bowbeer, David Holmes, Doug Lea
Print ISBN-10: 0-321-34960-1
źródło
Odpowiedzi:
Tworzenie wątków Java jest kosztowne, ponieważ wymaga sporo pracy:
Jest to również kosztowne w tym sensie, że nić ogranicza zasoby, dopóki żyje; np. stos wątków, dowolne obiekty dostępne ze stosu, deskryptory wątków JVM, natywne deskryptory wątków systemu operacyjnego.
Koszty wszystkich tych rzeczy zależą od platformy, ale nie są tanie na żadnej platformie Java, z którą się kiedykolwiek spotkałem.
Wyszukiwarka Google znalazła mi stary test porównawczy, który zgłasza szybkość tworzenia wątków wynoszącą ~ 4000 na sekundę w Sun Java 1.4.1 na vintage'owym podwójnym procesorze Xeon z 2002 roku z Linuxem z 2002 roku. Bardziej nowoczesna platforma da lepsze liczby ... i nie mogę komentować metodologii ... ale przynajmniej daje szansę na określenie, jak drogie może być tworzenie wątków.
Testy porównawcze Petera Lawreya wskazują, że tworzenie wątków jest obecnie znacznie szybsze w wartościach bezwzględnych, ale nie jest jasne, ile z tego wynika z usprawnień w Javie i / lub systemie operacyjnym ... lub wyższych prędkości procesora. Ale jego liczby wciąż wskazują na ponad 150-krotną poprawę, jeśli używasz puli wątków w porównaniu do tworzenia / rozpoczynania nowego wątku za każdym razem. (I podkreśla, że to wszystko jest względne ...)
(Powyższe zakłada „wątki rodzime” zamiast „zielone wątki”, ale wszystkie współczesne maszyny JVM używają wątków rodzimych ze względów wydajnościowych. Zielone wątki są prawdopodobnie tańsze w tworzeniu, ale płacisz za to w innych obszarach).
Zrobiłem trochę kopania, aby zobaczyć, jak naprawdę przydzielany jest stos wątku Java. W przypadku OpenJDK 6 w systemie Linux stos wątków jest przydzielany przez wywołanie,
pthread_create
które tworzy wątek macierzysty. (JVM nie przekazujepthread_create
wstępnie przydzielonego stosu.)Następnie w
pthread_create
ramach stosu jest przydzielane przez wywołaniemmap
w następujący sposób:Zgodnie z
man mmap
tymMAP_ANONYMOUS
flaga powoduje, że pamięć jest inicjowana do zera.Tak więc, chociaż może nie być konieczne zerowanie nowych stosów wątków Java (zgodnie ze specyfikacją JVM), w praktyce (przynajmniej w przypadku OpenJDK 6 w systemie Linux) są zerowane.
źródło
malloc()
funkcja C , z której mogłaby korzystać JVM, nie gwarantuje zerowania przydzielonej pamięci (prawdopodobnie w celu uniknięcia takich problemów z wydajnością).mmap()
wywołanie są mapowane na stronę kopiowania przy zapisie na stronę zerową, więc ich inicjalizacja odbywa się nie wmmap()
sobie, ale po pierwszym zapisaniu stron , a następnie tylko jednej stronie w czas. Oznacza to, że kiedy wątek zaczyna się wykonywać, koszt ponoszony przez utworzony wątek, a nie wątek twórcy.Inni dyskutowali, skąd pochodzą koszty gwintowania. Ta odpowiedź wyjaśnia, dlaczego utworzenie wątku nie jest tak drogie w porównaniu z wieloma operacjami, ale stosunkowo drogie w porównaniu z alternatywnymi metodami wykonywania zadań, które są stosunkowo tańsze.
Najbardziej oczywistą alternatywą dla uruchomienia zadania w innym wątku jest uruchomienie zadania w tym samym wątku. Trudno to pojąć tym, którzy zakładają, że więcej wątków jest zawsze lepszych. Logika jest taka, że jeśli narzut związany z dodaniem zadania do innego wątku jest większy niż zaoszczędzony czas, wykonanie zadania w bieżącym wątku może być szybsze.
Inną alternatywą jest użycie puli wątków. Pula wątków może być bardziej wydajna z dwóch powodów. 1) ponownie wykorzystuje wątki już utworzone. 2) możesz dostroić / kontrolować liczbę wątków, aby zapewnić optymalną wydajność.
Następujący program drukuje ....
Jest to test dla trywialnego zadania, który uwidacznia koszty każdej opcji wątków. (To zadanie testowe jest rodzajem zadania, które najlepiej wykonać w bieżącym wątku.)
Jak widać, utworzenie nowego wątku kosztuje tylko ~ 70 µs. Można to uznać za trywialne w wielu, jeśli nie w większości przypadków użycia. Względnie mówiąc, jest on droższy niż alternatywy, aw niektórych sytuacjach lepszym rozwiązaniem jest pula wątków lub w ogóle nieużywanie wątków.
źródło
Teoretycznie zależy to od JVM. W praktyce każdy wątek ma stosunkowo dużą ilość pamięci stosu (myślę, że domyślnie 256 KB). Ponadto wątki są implementowane jako wątki systemu operacyjnego, więc ich utworzenie wymaga wywołania systemu operacyjnego, tj. Przełącznika kontekstu.
Zdaj sobie sprawę, że „kosztowny” w informatyce jest zawsze bardzo względny. Tworzenie wątków jest bardzo kosztowne w porównaniu do tworzenia większości obiektów, ale niezbyt drogie w porównaniu do losowego wyszukiwania na dysku twardym. Nie musisz unikać tworzenia wątków za wszelką cenę, ale tworzenie ich setek na sekundę nie jest mądrym posunięciem. W większości przypadków, jeśli twój projekt wymaga wielu wątków, powinieneś użyć puli wątków o ograniczonym rozmiarze.
źródło
K
= 1024 ik
= 1000.;) en.wikipedia.org/wiki/KibibyteIstnieją dwa rodzaje wątków:
Właściwe wątki : są to abstrakcje wokół podstawowych wątków systemu operacyjnego. Tworzenie wątków jest zatem tak samo kosztowne jak w systemie - zawsze jest narzut.
„Zielone” wątki : utworzone i zaplanowane przez JVM, są tańsze, ale nie dochodzi do właściwego paralelizmu. Zachowują się one jak wątki, ale są wykonywane w wątku JVM w systemie operacyjnym. Według mojej wiedzy nie są one często używane.
Największym czynnikiem, jaki mogę wymyślić w narzutach związanych z tworzeniem wątków, jest rozmiar stosu , który zdefiniowałeś dla swoich wątków. Rozmiar stosu wątków można przekazać jako parametr podczas uruchamiania maszyny wirtualnej.
Poza tym tworzenie wątków zależy głównie od systemu operacyjnego, a nawet od implementacji maszyny wirtualnej.
Teraz pozwól mi coś wskazać: tworzenie wątków jest kosztowne, jeśli planujesz wystrzelić 2000 wątków na sekundę, co sekundę swojego środowiska wykonawczego. JVM nie jest do tego przystosowany . Jeśli będziesz miał kilku stabilnych pracowników, którzy nie będą zwalniani i zabijani w kółko, zrelaksuj się.
źródło
Tworzenie
Threads
wymaga przydzielenia sporej ilości pamięci, ponieważ musi to zrobić nie jeden, ale dwa nowe stosy (jeden dla kodu java, drugi dla kodu natywnego). Użycie executorów / pul wątków może uniknąć obciążenia, poprzez ponowne użycie wątków dla wielu zadań dla executora .źródło
Oczywiście sednem pytania jest to, co znaczy „drogi”.
Wątek musi utworzyć stos i zainicjować stos na podstawie metody uruchamiania.
Musi skonfigurować struktury statusu kontroli, tj. Jaki stan jest w stanie uruchomić, czeka itp.
Prawdopodobnie istnieje sporo synchronizacji przy konfigurowaniu tych rzeczy.
źródło