Daemon Thread – wątek demoniczny

Chociaż tytuł wpisu brzmi bardzo złowieszczo to nie zapowiada on niczego złego. Pojęcie wątków demonicznych pojawiło się już we wpisie związanym z ThreadFactory, natomiast dziś chciałbym przedstawić wam jak tworzyć takie wątki. Ponadto odpowiemy sobie na pytanie kiedy warto stosować ten typ zadań.

Różnice

W naszych aplikacjach mamy dwa typy wątków. Jednym z nich są wątki klienckie (na przykład wątek main), natomiast drugim typem są wątki demoniczne. Maszyna wirtualna (JVM) kończy swoją pracę wtedy, gdy zakończy się ostatni wątek kliencki (niedemoniczny). Oznacza to, że jeśli w naszej aplikacji mamy wątki typu demon, aplikacja zamknie się pomimo tego, że jest wykonywana tam jakaś praca. Znając tą cechę, należy pamiętać, aby nie umieszczać tam żadnych zadań związanych z I/O!

Zastosowanie

Najczęściej w takich wątkach realizowane są zadania niezwiązane ściśle z logiką biznesową. Demony wykorzystywane są przez JVM do odśmiecania pamięci czy też do przechowywania referencji obiektów do usunięcia. W naszym przypadku możemy umieszczać tam zadania związane z monitoringiem aplikacji, który może być zakończony w dowolnym momencie.

Tworzenie

Określenie czy wątek ma być demoniczny musi odbyć się przed jego utworzeniem:

Thread daemonThread = new Thread(job);
daemonThread.setDaemon(true);
daemonThread.start();

Jeśli natomiast spróbujemy to zrobić po uruchomieniu wątku, dostaniemy wyjątek IllegalThreadStateException:

@Test
void shouldThrowIllegalThreadStateException() {
    // Given
    Thread thread = new Thread(() -> {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    // When
    thread.start();
    // Then
    assertThrows(IllegalThreadStateException.class, () -> thread.setDaemon(true));
}

Typ wątku dziedziczony jest z wątku z jakiego został stworzony. Głównym wątkiem jest wątek main, który jest wątkiem klienckim. Oznacza to, że każdy tworzonym wątek jest domyślnie wątkiem klienckim:

@Test
void shouldCreateClientThread() {
    // Given
    Thread thread = new Thread(() -> {});
    thread.start();
    // When
    boolean daemon = thread.isDaemon();
    // Then
    assertThat(daemon).isFalse();
}

Testowanie

Dodajmy jeszcze prosty test sprawdzający czy wątek na pewno został utworzony jako demoniczny:

@Test
void shouldCreateDaemonThreadFromDaemonThread() throws InterruptedException {
    // Given
    DaemonThread thread = new DaemonThread();
    thread.setDaemon(true);
    thread.start();
    thread.join();
    // When
    boolean daemon = thread.getDaemon().isDaemon();
    // Then
    assertThat(daemon).isTrue();
}

@Test
void shouldCreateDaemonThread() {
    // Given
    Thread thread = new Thread(() -> {});
    thread.setDaemon(true);
    thread.start();
    // When
    boolean daemon = thread.isDaemon();
    // Then
    assertThat(daemon).isTrue();
}

Pula wątków

Jak pisałem we wstępie, kiedyś pojawił się już artykuł o ThreadFactory. Dzięki tej klasie, możemy określić, aby została utworzona pula wątków, w której każdy wątek jest demoniczny:

ThreadFactory factory = new ThreadFactoryBuilder()
        .setNameFormat("task-name-%d")
        .setDaemon(true)
        .build();

ExecutorService executorService = Executors.newFixedThreadPool(numberOfThread, factory);

Github

Całość jak zawsze na GitHubie.

  • Bartek

    When w tych testach to uruchomienie wątku, nie przypisanie zmiennej.