CodeGym /Kursy /JAVA 25 SELF /Parametry i priorytety wątków

Parametry i priorytety wątków

JAVA 25 SELF
Poziom 51 , Lekcja 3
Dostępny

1. Przekazywanie parametrów do wątków

Gdy uruchamiasz wątek, często chcesz przekazać mu jakąś „pracę”: nazwę pliku, zakres liczb albo pozdrowienie. Wątek bez parametrów — to jak kurier bez adresu: biega po mieście, ale nie wie, dokąd dostarczyć pizzę.

Jak to zrobić?

W Javie wątki tworzy się zwykle na dwa sposoby:

  • Dziedziczenie po klasie Thread
  • Implementacja interfejsu Runnable (lub użycie wyrażenia lambda)

Parametry najczęściej przekazuje się przez konstruktor klasy implementującej Runnable. To proste, bezpieczne i zrozumiałe. Oznacz pola jako final — i ochronisz parametry przed zmianami z innych wątków. Używanie setterów/publicznych pól grozi problemami z synchronizacją.

Przykład 1: Wątek z parametrem przekazywanym przez konstruktor

Rozbudujmy aplikację szkoleniową: przetwarzanie zamówień użytkowników w osobnym wątku.

public class OrderProcessor implements Runnable {
    private final String orderId;

    public OrderProcessor(String orderId) {
        this.orderId = orderId;
    }

    @Override
    public void run() {
        System.out.println("Przetwarzam zamówienie: " + orderId + " w wątku " + Thread.currentThread().getName());
        // Tutaj może być długie przetwarzanie...
    }
}

Tworzymy i uruchamiamy wątki:

public class Main {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new OrderProcessor("ORDER-001"));
        Thread thread2 = new Thread(new OrderProcessor("ORDER-002"));
        thread1.start();
        thread2.start();
    }
}

Oczekiwany wynik:

Przetwarzam zamówienie: ORDER-001 w wątku Thread-0
Przetwarzam zamówienie: ORDER-002 w wątku Thread-1

Przykład 2: Wątek z parametrem przez wyrażenie lambda (Java 8+)

Jeśli zadanie jest proste, można obejść się bez osobnej klasy.

public class Main {
    public static void main(String[] args) {
        String user1 = "Alice";
        String user2 = "Bob";

        Thread thread1 = new Thread(() -> {
            System.out.println("Cześć, " + user1 + " z " + Thread.currentThread().getName());
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Cześć, " + user2 + " z " + Thread.currentThread().getName());
        });

        thread1.start();
        thread2.start();
    }
}

Dlaczego nie należy używać setterów/getterów do przekazywania parametrów?
Jeśli przekazujesz parametry przez settery lub publiczne pola, istnieje ryzyko, że inny wątek zmieni wartość w trakcie wykonywania. Może to prowadzić do trudnych do wykrycia błędów. Lepiej oznaczyć pola jako final i przekazywać je przez konstruktor.

2. Priorytety wątków

W Javie każdy wątek ma priorytet — liczbę całkowitą od 1 do 10, która mówi planiscie (schedulerowi), jak „ważny” jest dany wątek w porównaniu z innymi. Domyślnie wszystkie wątki mają priorytet 5 (Thread.NORM_PRIORITY).

Ważne: priorytet to tylko „sugestia” dla systemu operacyjnego. To nie jest gwarancja, że wątek z priorytetem 10 będzie działał szybciej niż wątek z priorytetem 1. Wszystko zależy od systemu, jego ustawień i bieżącego obciążenia.

Jak ustawić priorytet wątku?

Klasa Thread ma metody:

  • setPriority(int newPriority)
  • getPriority()

I trzy standardowe stałe:

  • Thread.MIN_PRIORITY (1)
  • Thread.NORM_PRIORITY (5)
  • Thread.MAX_PRIORITY (10)

Przykład 3: Ustawianie priorytetu wątku

public class PriorityDemo {
    public static void main(String[] args) {
        Runnable task = () -> {
            System.out.println("Wątek " + Thread.currentThread().getName() +
                    " z priorytetem " + Thread.currentThread().getPriority());
        };

        Thread low = new Thread(task, "LowPriority");
        Thread norm = new Thread(task, "NormalPriority");
        Thread high = new Thread(task, "HighPriority");

        low.setPriority(Thread.MIN_PRIORITY);    // 1
        norm.setPriority(Thread.NORM_PRIORITY);  // 5
        high.setPriority(Thread.MAX_PRIORITY);   // 10

        low.start();
        norm.start();
        high.start();
    }
}

Oczekiwany wynik (kolejność linii nie jest gwarantowana!):

Wątek LowPriority z priorytetem 1
Wątek HighPriority z priorytetem 10
Wątek NormalPriority z priorytetem 5

Czy priorytet wpływa na kolejność wykonywania?

W większości przypadków — nie. Priorytety mogą wpływać na to, ile czasu procesor przydziela wątkowi, ale nie gwarantują kolejności startu ani zakończenia. Nie buduj logiki programu na priorytetach — traktuj je jako „miękkie” podpowiedzi, np. aby wątek w tle nie przeszkadzał głównemu.

Tabela: Stałe priorytetów wątków

Stała Wartość Opis
Thread.MIN_PRIORITY
1 Najniższy priorytet
Thread.NORM_PRIORITY
5 Normalny priorytet
Thread.MAX_PRIORITY
10 Najwyższy priorytet

3. Nazywanie wątków

Nazwa wątku to „przydomek” w świecie wielozadaniowości. Gdy masz dziesiątki wątków, łatwiej debugować, jeśli zamiast "Thread-7" w logu zobaczysz "FileUploader-1". To szczególnie ważne podczas analizy logów i szukania błędów.

Jak ustawić nazwę wątku?

Nazwę można podać bezpośrednio w konstruktorze Thread albo ustawić metodą setName. Bieżącą nazwę można pobrać przez getName.

Thread t = new Thread(() -> {
    System.out.println("Pracuję!");
}, "MyThreadName");
t.start();

Przykład 4: Nazwane wątki

public class NamedThreads {
    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            System.out.println("Jestem wątkiem: " + Thread.currentThread().getName());
        }, "Downloader");

        Thread threadB = new Thread(() -> {
            System.out.println("Jestem wątkiem: " + Thread.currentThread().getName());
        });
        threadB.setName("Uploader");

        threadA.start();
        threadB.start();
    }
}

Oczekiwany wynik:

Jestem wątkiem: Downloader
Jestem wątkiem: Uploader

4. Praktyka: kilka wątków z różnymi parametrami, priorytetami i nazwami

Połączmy wszystko w jednym przykładzie: każde zamówienie — osobny wątek z własną nazwą i priorytetem.

public class OrderProcessor implements Runnable {
    private final String orderId;
    private final int processingTimeMs;

    public OrderProcessor(String orderId, int processingTimeMs) {
        this.orderId = orderId;
        this.processingTimeMs = processingTimeMs;
    }

    @Override
    public void run() {
        System.out.println("[" + Thread.currentThread().getName() + "] Rozpocząłem przetwarzanie zamówienia " + orderId +
                " (priorytet " + Thread.currentThread().getPriority() + ")");
        try {
            Thread.sleep(processingTimeMs); // imitacja pracy
        } catch (InterruptedException e) {
            System.out.println("[" + Thread.currentThread().getName() + "] Przerwany!");
        }
        System.out.println("[" + Thread.currentThread().getName() + "] Zakończyłem przetwarzanie zamówienia " + orderId);
    }

    public static void main(String[] args) {
        Thread fastOrder = new Thread(new OrderProcessor("FAST-ORDER", 500), "FastOrderThread");
        Thread normalOrder = new Thread(new OrderProcessor("NORMAL-ORDER", 1000), "NormalOrderThread");
        Thread slowOrder = new Thread(new OrderProcessor("SLOW-ORDER", 2000), "SlowOrderThread");

        fastOrder.setPriority(Thread.MAX_PRIORITY);
        normalOrder.setPriority(Thread.NORM_PRIORITY);
        slowOrder.setPriority(Thread.MIN_PRIORITY);

        fastOrder.start();
        normalOrder.start();
        slowOrder.start();
    }
}

Co zobaczymy:

  • Każdy wątek wypisuje, które zamówienie przetwarza, swoją nazwę i priorytet.
  • Czas przetwarzania jest symulowany za pomocą Thread.sleep.
  • Kolejność zakończenia wątków może nie pokrywać się z priorytetem.

5. Ważne niuanse i szczegóły

Przekazywanie parametrów: tylko przez konstruktor!

Tak jest bezpieczniej. Oznacz parametry jako final — nie będzie można ich zmienić po utworzeniu obiektu i żaden inny wątek nie „podstawi” swoich wartości. To kluczowe, by zapobiegać wyścigom danych.

Priorytety: nie buduj na nich logiki biznesowej

Priorytety to jak prośba do kelnera: „Czy mogę dostać kawę szybciej?”. Czasem działa, czasem — nie. To wskazówka dla systemu operacyjnego, a nie reguła wykonania.

Nazwy wątków: używaj do debugowania

Daje ogromną oszczędność czasu podczas analizy logów. Wyrób sobie nawyk nadawania od razu sensownych nazw.

6. Typowe błędy przy pracy z parametrami i priorytetami wątków

Błąd nr 1: Przekazywanie parametrów przez pola publiczne. Jeśli deklarujesz parametry jako zwykłe pola i zmieniasz je po starcie wątku, możesz otrzymać nieprzewidywalne rezultaty. Używaj pól final i konstruktora.

Błąd nr 2: Oczekiwanie, że priorytet gwarantuje kolejność. Ustawiłeś Thread.MAX_PRIORITY — i myślisz, że wątek zawsze będzie pierwszy? Nie. Nie używaj priorytetów do synchronizacji ani sterowania logiką.

Błąd nr 3: Nienazwane wątki w logach. Gdy w logach są tylko „Thread-3”, „Thread-7” — trudno znaleźć winowajcę. Nadawaj wątkom sensowne nazwy przy pomocy konstruktora lub setName.

Błąd nr 4: Używanie tego samego obiektu Runnable dla wielu wątków. Jeśli obiekt Runnable przechowuje stan i przekazujesz go wielu wątkom, „dzielą” one parametry — prosta droga do wyścigów danych. Twórz osobny Runnable na wątek.

Błąd nr 5: Przekazywanie parametrów przez metody set*. Jeśli ustawiasz parametry przez set* już po utworzeniu/uruchomieniu wątku, wartości mogą zmienić się „w locie” — klasyczny race condition.

1
Zadanie
JAVA 25 SELF, poziom 51, lekcja 3
Niedostępne
Zarządzanie agentami na internetowym marketplace 🛍️
Zarządzanie agentami na internetowym marketplace 🛍️
1
Zadanie
JAVA 25 SELF, poziom 51, lekcja 3
Niedostępne
Koordynacja robotów na inteligentnej fabryce 🏭
Koordynacja robotów na inteligentnej fabryce 🏭
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION