Stany wątków są częstym pytaniem na rozmowie kwalifikacyjnej z działu wielowątkowości. W Javie wątki mogą znajdować się w sześciu stanach. Każda zmiana stanu poprzedzona jest jakimś zdarzeniem. Co to za zdarzenia? Zapraszam do wpisu, aby się o tym dowiedzieć!

Stan

Aby pobrać stan wątku wykorzystujemy wbudowaną metodę getState(), która znajduje się w klasie Thread:

1
2
final Thread newThread = new Thread();
newThread.getState();

Jak napisałem we wstępie, wątek może znajdować się w jednym z sześciu stanów:

NEW

Wątek w stanie NEW znajduje się zaraz po utworzeniu. Posiada on ten stan dopóki nie uruchomimy metody start():

1
2
3
4
5
6
7
@Test
void shouldReturnNewState() {
// Given
final Thread newThread = new Thread();
// Then
assertThat(newThread.getState()).isEqualTo(Thread.State.NEW);
}

RUNNABLE

Po wywołaniu metody start() na wątku, zmienia on swój stan z NEW na RUNNABLE:

1
2
3
4
5
6
7
8
9
10
@Test
void shouldReturnRunnableState() throws InterruptedException {
// Given
final Thread runnableThread = new Thread(() -> {
assertThat(Thread.currentThread().getState()).isEqualTo(Thread.State.RUNNABLE);
});
// When
runnableThread.start();
runnableThread.join();
}

BLOCKED

Wątek w stanie BLOCKED może znaleźć się wtedy, kiedy dostęp do sekcji krytycznej jest aktualnie zajęty (znajduje się blokada na monitorze). Innymi słowy, kiedy wątek będzie próbował się dostać do synchronizowanej metody, a wewnątrz znajduje się już inny wątek, to stan zmieniany jest na BLOCKED:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
void shouldReturnBlockedState() throws InterruptedException {
// Given
final Thread synchronizedThread = new Thread(this::synchronizedMethod);
final Thread blockedThread = new Thread(this::synchronizedMethod);

// When
synchronizedThread.start();
blockedThread.start();

// Then
Thread.sleep(10);
assertThat(blockedThread.getState()).isEqualTo(Thread.State.BLOCKED);
}

WAITING

Zmiana wątku na stan WAITING odbywa się wtedy, gdy wątek czeka na inne wątki. Nie jest to czekanie jak w sekcji krytycznej, tylko czekanie wywołane przykładowo metodą wait() czy join():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
void shouldReturnWaitingState() throws InterruptedException {
// Given
final Thread waitingThread = new Thread(this::synchronizedMethod);
final Thread wrapperThread = new Thread(() -> {
waitingThread.start();
try {
Thread.currentThread().join();
} catch(InterruptedException e) {
e.printStackTrace();
}
});

// When
wrapperThread.start();

// Then
Thread.sleep(10);
assertThat(wrapperThread.getState()).isEqualTo(Thread.State.WAITING);
}

TIMED_WAITING

Każdemu z nas przydarzyło się przenieść wątek w stan uśpienia. Posiada on wtedy stan TIMED_WAITING. Ten stan pojawia się również, gdy używamy metod do czekania na inne wątki (jak wait() czy join()), które w parametrach przyjmują wartości czasowe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
void shouldReturnTimedWaitingState() throws InterruptedException {
// Given
final Thread timedWaitingThread = new Thread(() -> {
try {
Thread.sleep(1\_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});

// When
timedWaitingThread.start();

// Then
Thread.sleep(10);
assertThat(timedWaitingThread.getState()).isEqualTo(Thread.State.TIMED\_WAITING);
}

TERMINATED

Po zakończeniu swojej pracy (czyli po zakończeniu metody run()) wątek przechodzi w stan TERMINATED:

1
2
3
4
5
6
7
8
9
10
11
12
@Test
void shouldReturnTerminatedState() throws InterruptedException {
// Given
final Thread terminatedThread = new Thread(()->{});

// When
terminatedThread.start();
terminatedThread.join();

// Then
assertThat(terminatedThread.getState()).isEqualTo(Thread.State.TERMINATED);
}

Github

Całość jak zawsze na Githubie.