JavaRush /Blog Java /Random-PL /Doceniaj czas spędzony na streamach
Andrei
Poziom 2

Doceniaj czas spędzony na streamach

Opublikowano w grupie Random-PL

Przedmowa. Wujek Petya

Powiedzmy, że chcemy napełnić butelkę wodą. Dostępna jest butelka i kran z wodą wujka Petyi. Wujek Petya zamontował dziś nowy kran i ciągle wychwalał jego piękno. Wcześniej używał tylko starego, zatkanego kranu, więc kolejki przy butelkowaniu były ogromne. Po chwili grzebania słychać było od strony rozlewania się dźwięk nalewania wody, po 2 minutach butelka jest jeszcze w fazie napełniania, za nami utworzyła się zwykła kolejka, a w mojej głowie pojawia się obraz jak troskliwy Wujek Petya wybiera do naszej butelki tylko najlepsze cząsteczki H2O. Wujek Petya, wytrenowany przez życie, uspokaja szczególnie agresywnych i obiecuje skończyć jak najszybciej. Skończywszy butelkę, bierze następną i odkręca zwykłe ciśnienie, co nie ujawnia wszystkich możliwości nowego kranu. Ludzie nie są szczęśliwi... Docenianie czasu ze streamami - 1

Teoria

Wielowątkowość to zdolność platformy do tworzenia wielu wątków w ramach jednego procesu. Utworzenie i wykonanie wątku jest znacznie prostsze niż utworzenie procesu, więc jeśli zachodzi potrzeba realizacji kilku równoległych akcji w jednym programie, wykorzystywane są dodatkowe wątki. W JVM dowolny program działa w głównym wątku, a reszta jest z niego uruchamiana. W ramach tego samego procesu wątki mogą wymieniać między sobą dane. Rozpoczynając nowy wątek, możesz zadeklarować go jako wątek użytkownika za pomocą metody
setDaemon(true);
takie wątki zostaną automatycznie zakończone, jeśli nie ma już innych uruchomionych wątków. Wątki mają priorytet pracy (wybór priorytetu nie gwarantuje, że wątek o najwyższym priorytecie zakończy się szybciej niż wątek o niższym priorytecie).
  • MIN_PRIORITY
  • NORM_PRIORITY (domyślnie)
  • MAX_PRIORITY
Podstawowe metody pracy ze strumieniami:
  • run()– wykonuje wątek
  • start()– rozpoczyna wątek
  • getName()– zwraca nazwę wątku
  • setName()– określa nazwę strumienia
  • wait()– metoda dziedziczona, wątek czeka na wywołanie metody notify()z innego wątku
  • notify()– metoda dziedziczona, wznawia zatrzymany wcześniej wątek
  • notifyAll()– metoda dziedziczona, wznawia wcześniej zatrzymane wątki
  • sleep()– wstrzymuje transmisję na określony czas
  • join()– czeka na zakończenie wątku
  • interrupt()– przerywa wykonywanie wątku
Więcej metod znajdziesz tutaj.Czas pomyśleć o nowych wątkach jeśli Twój program posiada:
  • Dostęp do sieci
  • Dostęp do systemu plików
  • graficzny interfejs użytkownika

Klasa wątku

Wątki w Javie są reprezentowane jako klasa Threadi jej potomkowie. Poniższy przykład jest prostą implementacją klasy stream.
import static java.lang.System.out;

public class ExampleThread extends Thread{

    public static void main(String[] args) {
        out.println("Основной поток");
        new ExampleThread().start();
    }

    @Override
    public void run() {
        out.println("Новый поток");
    }
}
W rezultacie otrzymujemy
Основной поток
Новый поток
Tutaj tworzymy naszą klasę i czynimy ją potomkiem klasy Thread, po czym piszemy metodę main(), aby uruchomić główny wątek i zastąpić metodę run()klasy Thread. Teraz, po utworzeniu instancji naszej klasy i wykonaniu jej odziedziczonej metody, start()uruchomimy nowy wątek, w którym zostanie wykonane wszystko, co opisano w ciele metody run(). Brzmi skomplikowanie, ale patrząc na przykładowy kod, wszystko powinno być jasne.

Uruchomialny interfejs

Oracle sugeruje również zaimplementowanie interfejsu w celu uruchomienia nowego wątku Runnable, co daje nam większą elastyczność projektowania niż jedyne dostępne dziedziczenie w poprzednim przykładzie (jeśli spojrzysz na źródło klasy, Threadzobaczysz, że implementuje ono również interfejs Runnable). Skorzystajmy z zalecanej metody tworzenia nowego wątku.
import static java.lang.System.out;

public class ExampleRunnable implements Runnable {

    public static void main(String[] args) {
        out.println("Основной поток");
        new Thread(new ExampleRunnable()).start();
    }

    @Override
    public void run() {
        out.println("Новый поток");
    }
}
W rezultacie otrzymujemy
Основной поток
Новый поток
Przykłady są bardzo podobne, ponieważ Pisząc kod musieliśmy zaimplementować abstrakcyjną metodę run()opisaną w interfejsie Runnable. Zakładanie nowego wątku wygląda trochę inaczej. Stworzyliśmy instancję klasy, Threadprzekazując jako parametr referencję do instancji naszego interfejsu Runnable. To podejście pozwala na tworzenie nowych wątków bez bezpośredniego dziedziczenia klasy Thread.

Długie operacje

Poniższy przykład wyraźnie pokaże korzyści płynące z używania wielu wątków. Powiedzmy, że mamy proste zadanie, które wymaga kilku długich obliczeń. Przed tym artykułem rozwiązalibyśmy je metodą, main()być może dzieląc je na osobne metody dla ułatwienia percepcji, może nawet na klasy, ale istota byłaby taka sama. Wszystkie operacje będą wykonywane sekwencyjnie, jedna po drugiej. Zasymulujmy obliczenia o dużej wadze i zmierzmy czas ich wykonania.
public class ComputeClass {

    public static void main(String[] args) {
        // Узнаем стартовое время программы
        long startTime = System.currentTimeMillis();

        // Определяем долгосрочные операции
        for(double i = 0; i < 999999999; i++){
        }
        System.out.println("complete 1");
        for(double i = 0; i < 999999999; i++){
        }
        System.out.println("complete 2");
        for(double i = 0; i < 999999999; i++){
        }
        System.out.println("complete 3");

        //Вычисляем и выводим время выполнения программы
        long timeSpent = System.currentTimeMillis() - startTime;
        System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
    }
}
W rezultacie otrzymujemy
complete 1
complete 2
complete 3
программа выполнялась 9885 миллисекунд
Czas wykonania pozostawia wiele do życzenia i przez cały ten czas patrzymy na pusty ekran wyjściowy, a sytuacja jest bardzo podobna do historii o wujku Petyi, tyle że teraz w jego roli my, twórcy, nie skorzystaliśmy wszystkie możliwości nowoczesnych urządzeń. Poprawimy się.
public class ComputeClass {

    public static void main(String[] args) {
        // Узнаем стартовое время программы
        long startTime = System.currentTimeMillis();

        // Определяем долгосрочные операции
        new MyThread(1).start();
        new MyThread(2).start();
        for(double i = 0; i < 999999999; i++){
        }
        System.out.println("complete 3");

        //Вычисляем и выводим время выполнения программы
        long timeSpent = System.currentTimeMillis() - startTime;
        System.out.println("программа выполнялась " + timeSpent + " миллисекунд");
    }
}

class MyThread extends Thread{
int n;

MyThread(int n){
    this.n = n;
}

    @Override
    public void run() {
        for(double i = 0; i < 999999999; i++){
        }
        System.out.println("complete " + n);
    }
}
W rezultacie otrzymujemy
complete 1
complete 2
complete 3
программа выполнялась 3466 миллисекунд
Czas działania został znacznie skrócony (efekt ten może nie zostać osiągnięty lub może nawet wydłużyć czas wykonania na procesorach nie obsługujących wielowątkowości). Warto zaznaczyć, że wątki mogą kończyć się nie po kolei, a jeśli programista potrzebuje przewidywalności działań, musi ją zaimplementować niezależnie dla konkretnego przypadku.

Grupy wątków

Wątki w Javie można łączyć w grupy; do tego służy klasa ThreadGroup. Grupy mogą obejmować zarówno pojedyncze wątki, jak i całe grupy. Może to być wygodne, jeśli trzeba przerwać przepływy związane na przykład z siecią w przypadku utraty połączenia. Więcej o grupach możesz przeczytać tutaj. Mam nadzieję, że teraz temat stał się dla Ciebie jaśniejszy i Twoi użytkownicy będą zadowoleni.
Docenianie czasu ze streamami - 2
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION