Jakie są główne zastosowania yield () i czym różni się od join () i interrupt ()?

Jestem trochę zdezorientowany użyciem metody yield() w Javie, a konkretnie w przykładzie poniżej. Czytałem też, że yield() jest 'używane do zapobiegania wykonywaniu wątku'.

Moje pytania to:

  1. Wierzę, że poniższy kod daje taki sam wynik zarówno podczas używania yield(), jak i gdy go nie używa. Czy to prawda?

  2. Jakie są w rzeczywistości główne zastosowania yield()?

  3. Czym różni się yield() od join() i Metody?

Przykład kodu:

public class MyRunnable implements Runnable {

   public static void main(String[] args) {
      Thread t = new Thread(new MyRunnable());
      t.start();

      for(int i=0; i<5; i++) {
          System.out.println("Inside main");
      }
   }

   public void run() {
      for(int i=0; i<5; i++) {
          System.out.println("Inside run");
          Thread.yield();
      }
   }
}

Uzyskuję to samo wyjście używając powyższego kodu zarówno z użyciem yield() jak i bez użycia:

Inside main
Inside main
Inside main
Inside main
Inside main
Inside run
Inside run
Inside run
Inside run
Inside run
Author: BradleyDotNET, 2011-08-08

9 answers

Źródło: http://www.javamex.com/tutorials/threads/yield.shtml

Okna

W implementacji Hotspot, sposób działania {[0] } mA zmiana pomiędzy Java 5 i Java 6.

W Javie 5, Thread.yield() wywołuje wywołanie Windows API Sleep(0). To ma specjalny efekt wyczyszczenia kwantu bieżącego wątku i umieszczenie go na końcu kolejki dla jego poziomu priorytetu. W innych słowa, Wszystkie runnable threads of the same priority (and those of greater priorytet) będzie miał szansę uruchomić przed kolejnym wątkiem biorąc pod uwagę czas procesora. Kiedy w końcu zostanie ponownie zaplanowany, wróci z pełną pełną kwant , ale nie "przenosi" żadnego z pozostały kwant z czasu poddania się. To zachowanie jest niewiele różni się od niezerowego snu, w którym wątek śpiący zwykle traci 1 wartość kwantową (w efekcie 1/3 kleszcza 10 lub 15ms).

W Javie 6 zachowanie to zostało zmienione. Hotspot vm wdraża teraz Thread.yield() korzystanie z wywołania API Windows SwitchToThread(). This call sprawia, że bieżący wątek rezygnuje z jego obecnych czasów, ale nie jego Cała kwant. Oznacza to, że w zależności od priorytetów innych wątki, wątek może być zaplanowany z powrotem w jednym przerwaniu okres później . (Zobacz sekcję planowanie wątków aby dowiedzieć się więcej informacje o terminach.)

Linux

Pod Linuksem Hotspot Po prostu wywołuje sched_yield(). Konsekwencje to wezwanie jest trochę inne i być może bardziej dotkliwe niż pod Windows:

  • inny wątek nie otrzyma kolejnego kawałka CPU dopóki wszystkie inne wątki nie będą miały kawałka CPU ;
  • W przeciwieństwie do wersji 2.6.8, w jądrze 2.6.8, wątek ten jest domyślnie brany pod uwagę przez heurystykę schedulera na najnowszym procesorze alokacji - czyli w domyśle wątek, który ma yielded może mieć więcej CPU, gdy zaplanowane w przyszłości.

(Więcej informacji na temat priorytetów można znaleźć w sekcji planowanie wątków i algorytmów planowania.)

Kiedy stosować yield()?

Powiedziałbym praktycznie nigdy . Jego zachowanie nie jest standardowo zdefiniowane i są ogólnie lepsze sposoby wykonywania zadań, które można może chcesz wykonać z yield ():

  • jeśli próbujesz użyć tylko części CPU , możesz to zrobić w bardziej kontrolowany sposób, szacując, ile CPU w wątku użyła w ostatnim kawałku przetwarzania, a następnie spała dla niektórych ilość czasu na wyrównanie: patrz metoda sleep () ;
  • Jeśli czekasz na proces lub zasób, aby zakończyć lub stać się dostępnym, istnieją bardziej skuteczne sposoby, aby to osiągnąć, np. używając join () do poczekaj na zakończenie kolejnego wątku, używając mechanizm wait/notify pozwalający jednemu wątkowi sygnalizować drugi że zadanie zostało wykonane, a najlepiej za pomocą jednego z Java 5 konstrukcje współbieżne, takie jak Semafor lub blokowanie kolejki.
 87
Author: Sathwick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-02 10:58:54

Widzę, że pytanie zostało reaktywowane z bounty, teraz pytanie, Jakie są praktyczne zastosowania yield. Podam przykład z mojego doświadczenia.

Jak wiemy, yield zmusza wywołujący wątek do rezygnacji z procesora, na którym jest uruchomiony, aby inny wątek mógł zostać uruchomiony. Jest to przydatne, gdy bieżący wątek zakończył na razie pracę, ale chce szybko wrócić na początek kolejki i sprawdzić, czy jakiś warunek się zmienił. Czym to się różni od zmiennej warunkowej? yield umożliwia szybszy powrót wątku do stanu uruchomionego. Podczas oczekiwania na zmienną warunkową wątek jest zawieszony i musi czekać na inny wątek, aby zasygnalizować, że powinien być kontynuowany. yield zasadniczo mówi "pozwól uruchomić inny wątek, ale pozwól mi wrócić do pracy bardzo szybko, ponieważ oczekuję, że coś zmieni się w moim stanie bardzo szybko". To wskazuje na ruchliwe spinning, gdzie warunek może się szybko zmienić, ale zawieszenie wątku byłoby / align = "left" /

Ale dość bełkotania, oto konkretny przykład: wzór równoległy wavefront. Podstawową instancją tego problemu jest obliczanie pojedynczych "Wysp" 1s w tablicy dwuwymiarowej wypełnionej 0s i 1s. "wyspa" to grupa komórek, które sąsiadują ze sobą w pionie lub poziomie: {]}

1 0 0 0
1 1 0 0
0 0 0 1
0 0 1 1
0 0 1 1

Tutaj mamy dwie wyspy 1s: góra-lewo i dół-prawo.

Prostym rozwiązaniem jest wykonanie pierwszego przejścia przez całą array and replace the 1 values with an incrementing counter that by the end each 1 was replace with its sequence number in row major order:

1 0 0 0
2 3 0 0
0 0 0 4
0 0 5 6
0 0 7 8

W następnym kroku każda wartość jest zastępowana przez minimum między sobą a wartościami sąsiadów:

1 0 0 0
1 1 0 0
0 0 0 4
0 0 4 4
0 0 4 4

Możemy teraz łatwo określić, że mamy dwie wyspy.

Część, którą chcemy uruchomić równolegle, to krok, w którym obliczamy minima. Bez wchodzenia w zbyt wiele szczegółów, każdy wątek dostaje rzędy w sposób przeplatany i opiera się na wartościach obliczonych przez wątek przetwarzający powyższy wiersz. W związku z tym każdy wątek musi nieznacznie pozostawać za wątkiem przetwarzającym poprzedni wiersz, ale musi również nadążyć w rozsądnym czasie. Więcej szczegółów i implementację przedstawiam w niniejszym dokumencie . Zwróć uwagę na użycie sleep(0), które jest mniej więcej odpowiednikiem C yield.

W tym przypadku yield został użyty w celu zmuszenia każdego wątku po kolei do wstrzymania, ale ponieważ wątek przetwarzanie sąsiedniego wiersza przyspieszyłoby w międzyczasie bardzo szybko, zmienna warunkowa okazałaby się katastrofalnym wyborem.

Jak widzisz, yield jest dość drobnoziarnistą optymalizacją. Użycie go w niewłaściwym miejscu np. czekanie na warunek, który sam się zmieni, spowoduje nadmierne użycie procesora.

Przepraszam za długi bełkot, mam nadzieję, że wyraziłem się jasno.
 31
Author: Tudor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 08:35:12

O różnicach między yield(), interrupt() i join() - ogólnie, nie tylko w Javie:

  1. poddanie się : dosłownie "poddanie się" oznacza odpuszczenie, poddanie się, poddanie się. Wydajny wątek mówi systemowi operacyjnemu (lub maszynie wirtualnej, lub co innego), że jest skłonny pozwolić innym wątkom być zaplanowane w jego miejsce. To wskazuje, że nie robi czegoś zbyt krytycznego. To tylko podpowiedź i nie gwarantuje żadnego efektu.
  2. dołączenie : Kiedy wiele wątków "łączy" na jakimś uchwycie, tokenie lub encji, wszystkie z nich czekają, aż wszystkie inne odpowiednie wątki zakończą wykonywanie (w całości lub na własne odpowiednie połączenie). Oznacza to, że kilka wątków wypełniło swoje zadania. Następnie każdy z tych wątków może być zaplanowany do kontynuowania innej pracy, będąc w stanie założyć, że wszystkie te zadania są rzeczywiście zakończone. (Nie mylić z łączami SQL!)
  3. interruption : używany przez jeden wątek do "szturchnięcia" innego wątku, który śpi, czeka lub dołącza - tak, że ma być zaplanowane, aby kontynuować pracę ponownie, być może ze wskazaniem, że zostało przerwane. (Nie mylić z przerwami sprzętowymi!)

W szczególności Java, Zobacz

  1. Dołączenie:

    Jak korzystać z wątku.dołączyć? (tutaj na StackOverflow)

    Kiedy łączyć wątki?

  2. Przerywanie:

    Jest Nitką.przerywać () (tutaj na StackOverflow)

 13
Author: einpoklum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:10

Po pierwsze, rzeczywisty opis to

Powoduje, że aktualnie wykonujący obiekt wątku tymczasowo wstrzymuje i Zezwalaj na wykonywanie innych wątków.

Jest bardzo prawdopodobne, że twój główny wątek uruchomi pętlę pięć razy przed wykonaniem metody run nowego wątku, więc wszystkie wywołania yield będą miały miejsce dopiero po wykonaniu pętli w głównym wątku.

join zatrzyma bieżący wątek, dopóki wątek nie zostanie wywołany przez join() jest wykonywana.

interrupt spowoduje przerwanie wątku, w którym jest wywoływany, powodując InterruptedException.

yield Umożliwia przełączanie kontekstu na inne wątki, więc ten wątek nie pochłonie całego wykorzystania procesora procesowego.

 9
Author: MByD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-13 00:51:04

Jakie są w rzeczywistości główne zastosowania yield ()?

Yield sugeruje procesorowi, że można zatrzymać bieżący wątek i rozpocząć wykonywanie wątków z wyższym priorytetem. Innymi słowy, przypisanie wartości niskiego priorytetu do bieżącego wątku, aby pozostawić miejsce dla bardziej krytycznych wątków.

Wierzę, że poniższy kod daje taki sam wynik zarówno podczas używania yield (), jak i gdy go nie używa. Czy to prawda?

Nie, te dwa wyniki będą różne. Bez funkcji yield (), gdy wątek uzyska kontrolę, uruchomi pętlę 'Inside run' za jednym zamachem. Jednak przy użyciu metody yield(), gdy wątek uzyska kontrolę, wydrukuje "Inside run" raz, a następnie przekaże kontrolę innemu wątkowi, jeśli taka istnieje. Jeśli nie ma wątku w toku, wątek zostanie wznowiony ponownie. Tak więc za każdym razem, gdy "Inside run" jest wykonywany, będzie szukał innych wątków do wykonania i jeśli żaden wątek nie jest dostępny, bieżący wątek będzie nadal wykonywany.

W Jaki Sposób jest yield () różni się od metod join() i interrupt ()?

Yield() służy do dawania miejsca innym ważnym wątkom, join() służy do oczekiwania na zakończenie wykonania kolejnego wątku, a interrupt () służy do przerwania aktualnie wykonującego wątku, aby zrobić coś innego.

 2
Author: abbas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-04 12:56:22

Aktualne odpowiedzi są nieaktualne i wymagają zmiany, biorąc pod uwagę ostatnie zmiany.

Nie ma praktycznej różnicy Thread.yield() pomiędzy wersjami Javy od 6 do 9.

TL; DR;

Wnioski oparte na kodzie źródłowym OpenJDK (http://hg.openjdk.java.net/).

Jeśli nie wziąć pod uwagę obsługi hotspot sond USDT (informacje o śledzeniu systemu opisane są w DTrace guide) i właściwości JVM ConvertYieldToSleep wtedy kod źródłowy {[8] } jest prawie taki sam. Zobacz Wyjaśnienie poniżej.

Java 9:

Thread.yield() wywołanie metody specyficznej dla systemu operacyjnego os::naked_yield():
Na Linuksie:

void os::naked_yield() {
    sched_yield();
}

W Systemie Windows:

void os::naked_yield() {
    SwitchToThread();
}

Java 8 i wcześniejsze:

Thread.yield() wywołanie metody specyficznej dla systemu operacyjnego os::yield():
Na Linuksie:

void os::yield() {
    sched_yield();
}

W Systemie Windows:

void os::yield() {  os::NakedYield(); }

Jak widzisz, Thread.yeald() w Linuksie jest identyczny dla wszystkich wersji Javy.
Zobaczmy Windows os::NakedYield() z JDK 8:

os::YieldResult os::NakedYield() {
    // Use either SwitchToThread() or Sleep(0)
    // Consider passing back the return value from SwitchToThread().
    if (os::Kernel32Dll::SwitchToThreadAvailable()) {
        return SwitchToThread() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
    } else {
        Sleep(0);
    }
    return os::YIELD_UNKNOWN ;
}

Różnica pomiędzy Javą 9 a Javą 8 w dodatkowym sprawdzeniu istnienia metody SwitchToThread() API Win32. Ten sam kod występuje w Javie 6.
Kod źródłowy os::NakedYield() w JDK 7 jest nieco inny, ale ma takie samo zachowanie:

    os::YieldResult os::NakedYield() {
    // Use either SwitchToThread() or Sleep(0)
    // Consider passing back the return value from SwitchToThread().
    // We use GetProcAddress() as ancient Win9X versions of windows doen't support SwitchToThread.
    // In that case we revert to Sleep(0).
    static volatile STTSignature stt = (STTSignature) 1 ;

    if (stt == ((STTSignature) 1)) {
        stt = (STTSignature) ::GetProcAddress (LoadLibrary ("Kernel32.dll"), "SwitchToThread") ;
        // It's OK if threads race during initialization as the operation above is idempotent.
    }
    if (stt != NULL) {
        return (*stt)() ? os::YIELD_SWITCHED : os::YIELD_NONEREADY ;
    } else {
        Sleep (0) ;
    }
    return os::YIELD_UNKNOWN ;
}

Dodatkowe sprawdzenie zostało odrzucone z powodu metody SwitchToThread() są dostępne od Windows XP i Windows Server 2003(zobacz MSDN notes).

 2
Author: Gregory.K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-13 14:42:29

Thread.yield() powoduje przejście wątku ze stanu "Running" do stanu" Runnable". Uwaga: nie powoduje, że wątek przechodzi w stan "oczekiwania".

 0
Author: Prashant Gunjal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-05 18:16:03

Wątek.yield ()

Kiedy wywołujemy wątek.metoda yield (), scheduler wątków utrzymuje aktualnie uruchomiony wątek do stanu Runnable i wybiera inny wątek o takim samym lub wyższym priorytecie. Jeśli nie ma wątku o równym i wyższym priorytecie, to zmienia on wywołanie wątku yield (). Pamiętaj metoda wydajności nie powoduje, że wątek przejdzie do stanu oczekiwania lub zablokowania. Może tylko utworzyć wątek ze stanu Runnable do stanu Runnable.

Join ()

Gdy join jest wywoływane przez instancję wątku, ten wątek powie aktualnie wykonującemu wątek, aby poczekał na zakończenie wątku. Join jest używany w sytuacjach, gdy zadanie, które powinno zostać ukończone przed zakończeniem bieżącego zadania.

 -1
Author: Prashant Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-12 10:14:43

Yield () służy głównie do wstrzymania aplikacji wielowątkowej.

Wszystkie te różnice to yield() zatrzymuje wątek podczas wykonywania innego wątku i wraca po zakończeniu tego wątku, join() połączy początek wątku wykonując go do końca i innego wątku uruchomić po zakończeniu tego wątku, interrupt() zatrzyma wykonanie wątku na jakiś czas.

 -4
Author: K.Hughley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-01 20:43:11