Gerenciamento de Threads em Java
A classe Thread fornece diversos métodos para o controle e o gerenciamento de threads em execução. Entre eles, destacam-se os métodos sleep() e join(), que permitem pausar a execução e coordenar a finalização de threads de forma previsível.
Método sleep
O método estático Thread.sleep() suspende temporariamente a execução da thread atual por um período especificado de tempo. Existem três versões desse método:
static void sleep(long millis)
static void sleep(long millis, int nanos)
static void sleep(Duration duration)A primeira versão recebe como parâmetro o número de milissegundos.
A segunda adiciona um valor em nanossegundos ao tempo total de pausa.
A terceira utiliza um objeto Duration para representar a duração da suspensão.
Esse método pode lançar uma exceção do tipo InterruptedException, o que significa que o bloqueio pode ser interrompido antes de seu término. Por isso, o código que chama sleep() deve tratar ou declarar essa exceção.
Exemplo:
class MyTask implements Runnable {
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " started...");
System.out.println(name + " works");
try {
Thread.sleep(1000); // pausa por 1000 milissegundos
}
catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
System.out.println(name + " finished...");
}
}
public class Program {
public static void main(String[] args) {
System.out.println("Main thread started...");
var task = new MyTask();
new Thread(task, "MyTask").start();
System.out.println("Main thread finished...");
}
}No exemplo acima, o método run() contém uma pausa de um segundo. Quando a execução chega a Thread.sleep(1000), a thread é temporariamente suspensa e retomará sua execução apenas após o tempo determinado.
A saída será semelhante a:
Main thread started... Main thread finished... MyTask started... MyTask works MyTask finished...
Método join
Nos exemplos anteriores, a thread principal (main) terminava antes das threads criadas. Em muitos casos, é desejável que a thread principal finalize apenas depois que todas as threads secundárias concluírem suas tarefas. Para isso, existe o método join().
Esse método também possui variações:
void join()
void join(long millis)
void join(long millis, int nanos)
boolean join(Duration duration)Independentemente da versão, join() faz com que a thread que o chama aguarde a finalização da thread sobre a qual foi aplicado. Caso um tempo limite seja definido, a espera não ultrapassará esse intervalo.
Exemplo:
class MyTask implements Runnable {
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " started...");
System.out.println(name + " works");
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
System.out.println(name + " finished...");
}
}
public class Program {
public static void main(String[] args) {
System.out.println("Main thread started...");
var task = new MyTask();
var t = new Thread(task, "MyTask");
t.start();
try {
t.join(); // a thread principal aguarda a finalização da thread t
}
catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
System.out.println("Main thread finished...");
}
}A saída será:
Main thread started... MyTask started... MyTask works MyTask finished... Main thread finished...
A chamada de join() faz com que a thread principal espere até que MyTask termine antes de continuar. Assim, o encerramento ocorre em ordem previsível.
Também é possível limitar o tempo de espera. No exemplo abaixo, a thread principal aguardará no máximo 500 milissegundos pela conclusão de MyTask:
var task = new MyTask();
var t = new Thread(task, "MyTask");
t.start();
try {
t.join(500); // espera no máximo 500 ms
}
catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}Como o tempo de execução da tarefa é maior que 500 milissegundos, a thread principal encerrará antes que MyTask termine.
Main thread started... MyTask started... MyTask works Main thread finished... MyTask finished...
Aguardando múltiplas threads
Se o programa cria várias threads e é necessário que a principal finalize apenas após todas elas concluírem, basta chamar join() para cada uma.
class MyTask implements Runnable {
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + " started...");
System.out.println(name + " works");
try {
Thread.sleep(1000);
}
catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
System.out.println(name + " finished...");
}
}
public class Program {
public static void main(String[] args) {
System.out.println("Main thread started...");
Thread[] tasks = new Thread[4];
for (int i = 0; i < tasks.length; i++) {
tasks[i] = new Thread(new MyTask(), "MyTask_" + i);
tasks[i].start();
}
try {
for (Thread t : tasks) t.join();
}
catch (InterruptedException ex) {
System.out.println(ex.getMessage());
}
System.out.println("Main thread finished...");
}
}A execução mostrará que a thread principal só termina após todas as threads secundárias:
Main thread started... MyTask_2 started... MyTask_1 started... MyTask_2 works MyTask_1 works MyTask_3 started... MyTask_3 works MyTask_0 started... MyTask_0 works MyTask_1 finished... MyTask_2 finished... MyTask_3 finished... MyTask_0 finished... Main thread finished...
Resumo
- O método
Thread.sleep()pausa temporariamente a execução da thread atual. - Esse método lança
InterruptedException, que deve ser tratado. - O método
join()faz uma thread aguardar a finalização de outra. - É possível definir um tempo máximo de espera ao usar
join(long millis). - Para coordenar múltiplas threads, é necessário chamar
join()para cada uma delas.