Este documento discute a criação e controle de threads em Java, incluindo como implementar interfaces Runnable, estender a classe Thread, iniciar novos threads, interromper threads, fazer threads dormirem e esperarem por outros threads. O documento também explica a diferença entre threads user e daemon.
1. Criação e controle
de threads
THREADSCONCORRÊNCIAE
PARALELISMOEMJAVA
Helder da Rocha (helder@summa.com.br)
1
2. 1. Criação e controle de threads
2. Acesso exclusivo e comunicação entre threads
3. Ciclo de vida, aplicações e boas práticas
4. Variáveis atômicas
5. Travas
6. Coleções
7. Sincronizadores
8. Executores e Futures
9. Paralelismo
10. CompletableFuture
THREADSCONCORRÊNCIAE
PARALELISMOEMJAVA
3. Thread
• Uma sequência contínua de instruções em execução.
• É similar a um processo (do sistema operacional) mas com escopo
limitado a uma aplicação.
• Pode executar em paralelo com outros threads
• Pode ser interrompido quantas vezes for necessário, sempre continuando
do ponto onde parou.
4. +time
Single thread Thread-1 Thread-2 Core-1 Core-2
Single core Single core Dual core
Task 1
Task 2
Task 2
Task 1
Task 1
Task 2
Threadsexecutandotarefas
5. +time
Single thread Thread-1 Thread-2 Core-1 Core-2
Single core Single core Dual core
Task 1
Task 2
Task 2
Task 1
Task 1
Task 2
Threadsexecutandotarefas
Se há um único thread para executar duas tarefas, uma
aplicação precisa executá-las em sequência.A segunda
tarefa só é iniciada quando a primeira terminar.
6. +time
Single thread Thread-1 Thread-2 Core-1 Core-2
Single core Single core Dual core
Task 1
Task 2
Task 2
Task 1
Task 1
Task 2
Threadsexecutandotarefas
Duas tarefas rodando
concorrentemente
podem demorar mais
tempo para terminar,
mas garantem maior
responsividade
O sistema operacional
é responsável pela
mudança de contexto
e decide quanto
tempo cada thread
ocupa a CPU
Duas tarefas podem
executar ao mesmo
tempo, mesmo que
haja apenas um
processador.
7. +time
Single thread Thread-1 Thread-2 Core-1 Core-2
Single core Single core Dual core
Task 1
Task 2
Task 2
Task 1
Task 1
Task 2
Threadsexecutandotarefas
Com dois processadores ou dois cores, as duas
tarefas podem executar ao mesmo tempo, sem a
necessidade de mudança de contexto.
Ainda existe um overhead que pode atrasar o início
das aplicações, mas duas tarefas longas
provavelmente terminarão mais rapidamente
quando rodam em mais de um core.
8. ThreadsemJava
• Representados pela classe java.lang.Thread.
• Qualquer programa em Java possui pelo menos um thread, que executa
as instruções do método main().
• O thread principal é chamado de “main”.
9. OobjetoThread
• Pode-se obter uma referência ao objeto Thread do main() chamando
Thread.currentThread() dentro do main() ou qualquer método
chamado diretamente por main():
Thread principal = Thread.currentThread();
• Agora é possível chamar métodos de instância da classe Thread.
System.out.println("Nome do thread: "
+ principal.getName()); // imprime main
System.out.println("Thread toString(): "
+ principal); // imprime [main, 5, main]
10. AinterfaceRunnable
• A partir do thread main, podemos criar outros threads que irão rodar em
paralelo ou disputar a CPU com o thread principal.
• Todo thread precisa de uma sequência de instruções) para executar
• O thread main executa automaticamente o conteúdo do método static
void main(String[]) disparado pela JVM
• Threads adicionais executam automaticamente o conteúdo do método
void run() de uma classe que implementa java.lang.Runnable
11. ImplementandoRunnable
• A interface java.lang.Runnable é uma interface funcional
• Uma implementação de Runnable que imprime uma linha de texto e outra com
o nome do thread que está executando as instruções.
• Threads recebem nomes e IDs automaticamente. Nomes podem ser alterados.
public interface Runnable {
void run();
}
public class RunnableHelloWorld implements Runnable {
@Override public void run() {
System.out.println("Hello world paralelo!");
System.out.println("Eu sou o thread: "
+ Thread.currentThread().getName());
}
}
12. RodandoRunnablenomesmothread
• Um objeto Runnable é um objeto Java qualquer
• O programa abaixo executa o método run() no thread principal (e
imprime também o nome deste thread):
public class ThreadExampleSync {
public static void main(String[] args) {
Runnable paralelo = new RunnableHelloWorld();
paralelo.run();
System.out.println("Thread principal: " +
Thread.currentThread().getName());
}
}
Hello world paralelo!
Eu sou o thread: main
Thread principal: main
Executando no thread main
13. ComoiniciarumnovoThread
• Para criar um novo Thread é preciso criar uma nova instância da classe
Thread, que recebe como argumento uma implementação de Runnable:
• Para iniciar (executar) o Thread e executar o conteúdo de run() através
desse novo Thread chame o método start() do Thread:
Runnable tarefa = new ImplementacaoDeRunnable();
Thread t = new Thread(tarefa);
t.start();
14. ExecutandoRunnableemnovoThread
• O programa abaixo cria um novo Thread com a tarefa Runnable e depois
o inicia com o método start():
public class ThreadExampleAsync {
public static void main(String[] args) {
Runnable paralelo = new RunnableHelloWorld();
Thread t1 = new Thread(paralelo);
t1.start();
System.out.println("Thread principal: " +
Thread.currentThread().getName());
}
} Thread principal: main
Hello world paralelo!
Eu sou o thread: Thread-0Executando no thread Thread-0
15. main() {
Runnable task = new RunnableHelloWorld();
Thread t1 = new Thread(task);
t1.start();
System.out.print("Fim");
}
run() {
for(int i = 0; i < 10000; i++)
System.out.println(i);
}
Thread: main
Thread: Thread-0
main is done
Thread-0 is done
JVM is done
JVM
ExecutandoRunnableemnovoThread
16. Outrasformasdecriarthreads
• Estendendo a classe Thread
• Usando classes internas ou anônimas para implementar tarefas
• Usando expressões lambda para implementar tarefas
17. EstendendoaclasseThread
public class ThreadExampleAsync5 {
static class HelloThread extends Thread {
@Override public void run() {
System.out.println("Hello world from thread "
+ this.getName());
}
}
public static void main(String[] args) {
new HelloThread().start();
System.out.println("Thread principal: "
+ Thread.currentThread().getName());
}
}
18. Tarefacomoclasseinterna
public class ThreadExampleAsync2 {
public static void main(String[] args) {
class HelloParalelo implements Runnable {
@Override public void run() {
System.out.println("Hello world paralelo!");
}
}
Thread t1 = new Thread(new HelloParalelo());
t1.start();
System.out.println("Thread principal: "
+ Thread.currentThread().getName());
}
}
19. Tarefacomoclasseanônima
public class ThreadExampleAsync3 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override public void run() {
System.out.println("Hello world paralelo!");
}
});
t1.start();
System.out.println("Thread principal: "
+ Thread.currentThread().getName());
}
}
20. Tarefacomoexpressãolambda
public class ThreadExampleAsync4 {
public static void main(String[] args) {
Thread t1 = new Thread(
() -> System.out.println("Hello world paralelo!") );
t1.start();
System.out.println("Thread principal: "
+ Thread.currentThread().getName());
}
}
21. Interrupçãodethreads
• Um thread só termina (normalmente) quando run() terminar.
• Métodos de interrupção não interrompem o thread. Ligam flag (INTERRUPT)
que deve ser usado para finalizá-lo (fazer run() terminar normalmente).
• Métodos de instância:
• void interrupt() – liga o flag INTERRUPT.
• boolean isInterrupted() – retorna true se INTERRUPT é true.
• Método estático (atua sobre Thread.currentThread()):
• static boolean interrupted() – retorna true se INTERRUPT for
true e em seguida muda INTERRUPT para false.
22. Threadqueterminacominterrupt()
public class InterruptRunnable implements Runnable {
@Override public void run() {
boolean interrupt = false;
while(!interrupt) {
interrupt = Thread.interrupted();
System.out.println(">INTERRUPT flag: " + interrupt);
}
System.out.println("INTERRUPTED flag: " + interrupt);
System.out.println("Thread "
+ Thread.currentThread().getName() + " is DONE!");
}
}
Repete enquanto
interrupt for false
23. maininterrompendoThread-0
public class InterruptFlagExample {
public static void main(String[] args) {
Runnable runnable = new InterruptRunnable();
Thread t1 = new Thread(runnable);
t1.start();
// outras tarefas executadas pelo thread principal
t1.interrupt(); // liga o flag de interrupção em t1
System.out.println("Thread "
+ Thread.currentThread().getName() + " is DONE!");
}
}
24. Pondothreadsparadormir
• O thread que está executando pode suspender sua execução por um
determinado tempo chamando Thread.sleep(milissegundos)
• Para dormir 1 segundo (1000 milissegundos):
• Thread.sleep(1000);
• Nesse intervalo, outros threads que estejam esperando acesso à CPU terão
oportunidade de executar, independente de sua prioridade.
• O método sleep() precisa lidar com InterruptedException
25. InterruptedException
• Será lançada se a flag INTERRUPT estiver ativada em um thread durante a
execução de sleep()
• Checked exception: precisa ser capturada ou declarada
• Isto pode ser usado para finalizar um thread, sem a necessidade de testar o
flag INTERRUPT
• Bloco try-catch pode capturar a exceção e finalizar o thread normalmente
• Bloco try-catch pode ignorar a exceção e a interrupção (a exceção
automaticamente muda o estado do INTERRUPT para false)
26. Finalizaçãocom
InterruptedException
public class RandomLetters implements Runnable {
@Override public void run() {
try {
while(true) {
System.out.print(" " + (char)('A' + new Random().nextInt(26)));
Thread.sleep(200);
}
} catch (InterruptedException e) {
System.out.println("n" + Thread.currentThread().getName() + " interrupted.");
System.out.println("INTERRUPTED flag: " + Thread.currentThread().isInterrupted());
}
System.out.println("Thread " + Thread.currentThread().getName() + " is DONE!");
}
}
public class InterruptSleepExample {
public static void main(String[] args) {
Thread t1 = new Thread(new RandomLetters());
t1.start();
// main thread executa suas tarefas
t1.interrupt(); // sets interrupt flag in t1
System.out.println("nThread " +
Thread.currentThread().getName() + " is DONE!");
}
}
27. Religandooflagdeinterrupção
• Se o código decidir não interromper o thread, outra parte do programa
poderá assumir essa responsabilidade (é preciso religar o flag)
public void run() {
while(true) { // este loop continua mesmo com interrupção
try {
// chamadas externas que poderão lidar com o INTERRUPT
Thread.sleep(100);
} catch(InterruptedException e) {
System.out.println("Thread interrompido que não será finalizado."));
Thread.currentThread().interrupt(); // IMPORTANTE!
}
}
System.out.println("Thread finalizado."));
}
InterruptedException
desliga flag de interrupção
Liga novamente o flag
de interrupção
28. Esperandothreadsterminarem
• O método join() faz um thread esperar que o outro termine
• Se um thread t1 chama t2.join(), t1 é suspenso até que t2 termine
• Como todos os métodos que suspendem threads, join() lança
InterruptedException, que precisa ser capturada ou declarada.
Thread t1 = new Thread(() -> {
for(int i = 0; i < 10000; i++) System.out.println(i);
});
t1.start();
System.out.println("Waiting for " + t1.getName());
t1.join();
System.out.println("Thread main is DONE!");
29. main() {
Runnable task = new RunnableLoop();
Thread t1 = new Thread(task);
t1.start();
t1.join();
}
run() {
for(int i = 0; i < 10000; i++)
System.out.println(i);
}
Thread: main
Thread: Thread-1
main is done
Thread-1 is done
JVM is done
JVM
WAITING
for
Thread-1
Esperandothreadsterminarem
30. Daemonthreads
• Dois tipos de threads: user threads e daemon threads.
• Daemon threads existem apenas para servir aos user threads e são
destruídos quando não houver user threads executando
• User threads: o thread main e todos os threads criados até agora neste curso
• Daemon threads: garbage collector, ForkJoinPool
• Todo thread nasce user thread, exceto se for criado por um daemon thread,
ou se o método setDaemon(true) for chamado antes de começar a executar.
31. Daemonthreads
main() {
Runnable task = new RunnableHelloWorld();
Thread t1 = new Thread(task);
t1.setDaemon(true);
t1.start();
}
run() {
for(int i = 0; i < 10000; i++)
System.out.println(i);
}
Thread: main
Thread: Thread-1
main is doneJVM is done
JVM
Thread-1 interrupted