Linguagem de Programação III
Multithreading

Professora: Bianca de Almeida Dantas
E-mail: bianca.dantas@ifms.edu.br
Site: www.biancadantas.com
TÓPICOS
• Threads           Essa aula e seus exemplos foram baseados no
                    capítulo 26 do livro Java - Como Programar, 8ª
   • Ciclo de vida Edição (Deitel & Deitel)
   • Agendamento
   • Criação e execução
   • Exemplo
• Produtor-Consumidor
• Multithreading com GUI
THREADS
• Linhas de execução dentro de um processo.
  Conhecidas como processos leves.
• Cada thread possui sua pilha de chamadas de
  métodos e contador de instruções, entretanto a
  troca de contexto entre threads de um mesmo
  processo é menos custosa.
• Permite a execução concorrente de trechos de
  código que independem entre si.
• Se mais de um núcleo de processamento estiver
  disponível, a execução pode ser paralela.
• Linhas de execução dentro de um processo.
  Conhecidas como processos leves.
• Cada thread possui sua pilha de chamadas de
  métodos e contador de instruções, entretanto a
  troca de contexto entre threads de um mesmo
  processo é menos custosa.
• Permite a execução concorrente de trechos de
  código que independem entre si.
• Se mais de um núcleo de processamento estiver
  disponível, a execução pode ser paralela.
• Linhas de execução dentro de um processo.
  Conhecidas como processos leves.
• Cada thread possui sua pilha de chamadas de
  métodos e contador de instruções, entretanto a
  troca de contexto entre threads de um mesmo
  processo é menos custosa.
• Permite a execução concorrente de trechos de
  código que independem entre si.
• Se mais de um núcleo de processamento estiver
  disponível, a execução pode ser paralela.
THREADS – Ciclo de Vida
• Novo: a thread acaba de ser criada e ainda não
  foi iniciada pela primeira vez.
• Executável: a thread está realizando o seu
  processamento ou pronta para executá-lo.
• Espera: a thread está esperando que outra thread
  realize uma tarefa. Volta para o estado executável
  apenas quando outra thread a notifica.
• Espera sincronizada: a thread está esperando por
  um intervalo máximo de tempo.
• Bloqueado: a thread tentou realizar uma tarefa
  que não pôde ser imediatamente completada, ela
  fica neste estado até que a tarefa seja
  completada e, então, transita para o estado
  executável.
• Terminado: uma thread entra neste estado
  quando finaliza seu processamento (com ou sem
  sucesso).
• O estado executável pode ser subdividido em dois
  estados separados operacionalmente: pronto e
  em execução.
• O SO oculta a diferença entre esses dois estados
  da JVM, que os vê unicamente como executáveis.
• Quando uma thread é criada e passa para o
  estado executável, ela está, na verdade, no
  estado pronto; quando ela está efetivamente
  executando, ela está no estado em execução.
• Despachar uma thread: quando uma thread tem
  um processador atribuído a ela.
• Geralmente, uma thread executa por um
  pequeno período de tempo, o chamado quantum
  ou fração de tempo.
• Quando o quantum termina, a thread passa
  novamente para o estado pronto.
THREADS – Agendamento
• Agendamento de threads é o processo que o
  sistema operacional utiliza para definir a ordem de
  execução de threads.
• O agendador de threads (thread scheduler), em
  geral, utiliza as prioridades associadas às threads
  para definir sua política de execução.
• Em Java, as prioridades variam entre MIN_PRIORITY
  (uma constante de 1) e MAX_PRIORITY (uma
  constante de 10). Threads, geralmente, são
  iniciadas com NORM_PRIORITY.
• As três constantes são definidas na classe Thread.
.......
THREADS – Criação e Execução
• Para especificar uma classe cujo código pode ser
  executado concorrentemente em uma aplicação,
  pode-se utilizar a interface Runnable.
• As threads podem ser instanciadas passando como
  argumento um objeto Runnable.
EXEMPLO
• Código do projeto Multithread2 enviado por e-
  mail....
pool-1-thread-1 escreveu o valor 1 em 0
Próximo índice = 1
pool-1-thread-2 escreveu o valor 11 em 0
Próximo índice = 2
pool-1-thread-1 escreveu o valor 2 em 1
Próximo índice = 3
pool-1-thread-2 escreveu o valor 12 em 2
Próximo índice = 4
pool-1-thread-1 escreveu o valor 3 em 3
Próximo índice = 5
pool-1-thread-2 escreveu o valor 13 em 4
Próximo índice = 6

Conteúdo do vetor:
[11, 2, 12, 3, 13, 0]
• Como as threads executam independentemente e
  compartilham um vetor, o resultado pode não ser
  como desejado.
• Pela saída anterior, vimos que a thread 2
  sobrescreveu o valor recém escrito pela thread 1.
• Situações como essa, nas quais não conseguimos
  garantir que o resultado será coerente e nas quais o
  resultado dependerá da ordem em que as threads
  serão escalonadas são chamadas de condições de
  corrida.
• O trecho de código no qual as diferentes threads
  acessam as variáveis compartilhadas
  (possivelmente, modificando-as) é chamado de
  seção crítica.
• Resumindo: o código visto não é seguro para
  threads.
• Para resolver o problema, transformaremos os
  trechos de acesso às variáveis compartilhadas em
  operações atômicas, impedindo que mais de uma
  thread execute tais instruções simultaneamente.
EXEMPLO
• Código do projeto Multithread2 alterado enviado
  por e-mail.
• Nesse código, fizemos com que o código do método
  que adiciona um elemento ao SimpleArray seja
  atômico. Em Java, isso é feito usando a palavra-
  chave synchronized.
• Quando uma thread está executando o corpo de
  um método synchronized, nenhuma outra pode
  executá-la antes da primeira terminar sua execução.
• A instrução synchronized também pode ser
  utilizada para trechos de código.
PRODUTOR-CONSUMIDOR
• Um problema clássico da computação que envolve
  processos (ou threads) que produzem recursos para
  serem consumidos por outros.
• Existe um buffer compartilhado para
  armazenamento dos recursos. O produtor precisa
  parar de produzir quando o buffer estiver cheio e o
  consumidor não pode consumir se não houver
  recurso disponível.
ABORDAGEM 1
• Utilizar um buffer compartilhado com apenas uma
  posição (classe BufferSemSincronizacao).
• Não há garantia sobre a ordem de execução das
  threads, logo, muitas vezes valores são produzidos
  com o buffer lotado ou, ainda, consumidos antes da
  produção, levando a resultados inconsistentes.
ABORDAGEM 2
• Utilizar um buffer compartilhado com apenas uma
  posição, mas utilizando ArrayBlockingQueue (classe
  BufferBloqueante).
• A classe ArrayBlockingQueue pertence ao pacote
  java.util.concurrent, segura para threads e que
  implementa a interface BlockingQueue (que
  estende a classe Queue) e declara os métodos put e
  take (equivalentes bloqueantes dos métodos offer
  e poll.
• put coloca um elemento no fim da fila bloqueando
  se a mesma estiver cheia.
• take retira um elemento do começo da fila
  bloqueando se a mesma estiver vazia.
• O tamanho do ArrayBlockingQueue é passado como
  argumento para o construtor e não é aumentado
  dinamicamente.
ABORDAGEM 3
• Utilizar um buffer compartilhado com apenas uma
  posição, mas utilizando métodos sincronizados
  (synchronized).
• Utiliza uma variável booleana ocupado que indica se o
  buffer está totalmente ocupado ou não.
• O produtor espera enquanto o buffer estiver lotado
  utilizando uma chamada ao método wait.
  Similarmente, o consumidor espera enquanto o buffer
  estiver vazio.
• O método wait faz com que o objeto que o executou
  libere implicitamente o bloqueio sobre o buffer.
• O produtor/consumidor só é liberado de sua
  espera quando for notificado de que o objeto
  compartilhado já foi utilizado; isso é feito com a
  chamada a notifyAll.
• O método notifyAll retira do estado de espera
  todas as threads que estiverem esperando pelo
  objeto compartilhado, fazendo com que elas
  passem ao estado executável e tentem
  readquirir o bloqueio.
ABORDAGEM 4
• Utilizar um buffer circular compartilhado com 4
  posições.
• Permite minimizar o tempo de espera das threads
  devido ao aumento das posições disponíveis para
  escrita e leitura.
• Utiliza:
   • uma variável auxiliar para controlar quantos
     elementos válidos realmente há no buffer em um
     determinado momento.
   • Dois índices para indicar onde será feita a próxima
     leitura ou a próxima escrita.
MULTITHREADING COM GUI
• Aplicativos GUI são desafiantes para programação
  multithreaded.
• Aplicativos swing possuem uma única thread, a
  thread de despacho de eventos, responsável por
  tratar as interações com os componentes GUI do
  aplicativo.
• Todas as tarefas que exigem interação com a GUI do
  aplicativo são colocadas em uma fila de eventos e
  executadas em sequência pela thread de despacho
  de eventos.
• Componentes GUI Swing não são seguros para
  threads.
• Segurança para threads em aplicativos GUI é obtida
  assegurando que os componentes Swing são
  acessado a partir de uma única thread (a de
  despacho de eventos). Essa técnica é chamada de
  confinamento de thread.
• Utilizando essa ideia, uma alternativa interessante é
  colocar as atividades de longa duração, que
  independem da interface enquanto são executadas,
  em threads separadas da de despacho de eventos.
• Essa alternativa evita que a thread de despacho de
  eventos fique sem responder enquanto espera pelo
  resultado de tais atividades.
• Para auxiliar na programação dessas atividades
  mais demoradas em threads separadas, o Java SE 6
  fornece a classe SwingWorker. Essa classe pode
  realizar cálculos demorados e, então, atualizar s
  componentes Swing a partir da thread de despacho
  de eventos com base nos resultados dos cálculos.
• SwingWorker implemente a interface Runnable.
• Métodos comuns de SwingWorker:
  • doInBackground: define o cálculo longo na thread
    trabalhadora;
  • done: executado na thread de despacho de eventos
    quando doInBackground retorna;
  • execute: agenda o objeto SwingWorker a ser
    executado em uma thread trabalhadora;
  • get: espera a conclusão do cálculo e retorna o seu
    resultado;
  • publish: envia resultados intermediários dos cálculos
    para processamento na thread de despacho de
    eventos.
• process: recebe os resultados intermediários e os
  processa na thread de despacho de eventos;
• setProgress: configura a propriedade de progresso
  para notificar os ouvintes de alteração de
  propriedade na thread de despacho de eventos de
  atualizações da barra de progresso.
EXEMPLO 1: FIBONACCI
• No exemplo, forneceremos uma opção para calcular os
  termos da sequência de Fibonacci um a um ou para
  calcular um termo específico (até o 92º) em uma
  thread trabalhadora.
• SwingWorker é uma classe genérica que recebe dois
  parâmetros: o primeiro é o tipo retornado pelo método
  doInBackground e o segundo é o tipo passado entre os
  métodos publish e process.
EXEMPLO 2: ÍMPARES
• No exemplo, calculamos todos os números ímpares
  menores que um valor inteiro fornecido como entrada.
• Para obtenção dos ímpares, utilizamos o crivo de
  Eratóstenes.
• Os resultados são exibidos em uma área de texto à
  medida em que são obtidos.
• Uma barra de progresso mostra o quanto do cálculo já
  foi concluído até um determinado momento.
REFERÊNCIAS
• Java Como Programar – 8ª Edição. Deitel &
  Deitel.

Aula sobre multithreading

  • 1.
    Linguagem de ProgramaçãoIII Multithreading Professora: Bianca de Almeida Dantas E-mail: bianca.dantas@ifms.edu.br Site: www.biancadantas.com
  • 2.
    TÓPICOS • Threads Essa aula e seus exemplos foram baseados no capítulo 26 do livro Java - Como Programar, 8ª • Ciclo de vida Edição (Deitel & Deitel) • Agendamento • Criação e execução • Exemplo • Produtor-Consumidor • Multithreading com GUI
  • 3.
    THREADS • Linhas deexecução dentro de um processo. Conhecidas como processos leves. • Cada thread possui sua pilha de chamadas de métodos e contador de instruções, entretanto a troca de contexto entre threads de um mesmo processo é menos custosa. • Permite a execução concorrente de trechos de código que independem entre si. • Se mais de um núcleo de processamento estiver disponível, a execução pode ser paralela.
  • 4.
    • Linhas deexecução dentro de um processo. Conhecidas como processos leves. • Cada thread possui sua pilha de chamadas de métodos e contador de instruções, entretanto a troca de contexto entre threads de um mesmo processo é menos custosa. • Permite a execução concorrente de trechos de código que independem entre si. • Se mais de um núcleo de processamento estiver disponível, a execução pode ser paralela.
  • 5.
    • Linhas deexecução dentro de um processo. Conhecidas como processos leves. • Cada thread possui sua pilha de chamadas de métodos e contador de instruções, entretanto a troca de contexto entre threads de um mesmo processo é menos custosa. • Permite a execução concorrente de trechos de código que independem entre si. • Se mais de um núcleo de processamento estiver disponível, a execução pode ser paralela.
  • 6.
  • 7.
    • Novo: athread acaba de ser criada e ainda não foi iniciada pela primeira vez. • Executável: a thread está realizando o seu processamento ou pronta para executá-lo. • Espera: a thread está esperando que outra thread realize uma tarefa. Volta para o estado executável apenas quando outra thread a notifica. • Espera sincronizada: a thread está esperando por um intervalo máximo de tempo.
  • 8.
    • Bloqueado: athread tentou realizar uma tarefa que não pôde ser imediatamente completada, ela fica neste estado até que a tarefa seja completada e, então, transita para o estado executável. • Terminado: uma thread entra neste estado quando finaliza seu processamento (com ou sem sucesso).
  • 9.
    • O estadoexecutável pode ser subdividido em dois estados separados operacionalmente: pronto e em execução. • O SO oculta a diferença entre esses dois estados da JVM, que os vê unicamente como executáveis. • Quando uma thread é criada e passa para o estado executável, ela está, na verdade, no estado pronto; quando ela está efetivamente executando, ela está no estado em execução.
  • 10.
    • Despachar umathread: quando uma thread tem um processador atribuído a ela. • Geralmente, uma thread executa por um pequeno período de tempo, o chamado quantum ou fração de tempo. • Quando o quantum termina, a thread passa novamente para o estado pronto.
  • 12.
    THREADS – Agendamento •Agendamento de threads é o processo que o sistema operacional utiliza para definir a ordem de execução de threads. • O agendador de threads (thread scheduler), em geral, utiliza as prioridades associadas às threads para definir sua política de execução. • Em Java, as prioridades variam entre MIN_PRIORITY (uma constante de 1) e MAX_PRIORITY (uma constante de 10). Threads, geralmente, são iniciadas com NORM_PRIORITY. • As três constantes são definidas na classe Thread.
  • 13.
  • 14.
    THREADS – Criaçãoe Execução • Para especificar uma classe cujo código pode ser executado concorrentemente em uma aplicação, pode-se utilizar a interface Runnable. • As threads podem ser instanciadas passando como argumento um objeto Runnable.
  • 15.
    EXEMPLO • Código doprojeto Multithread2 enviado por e- mail....
  • 16.
    pool-1-thread-1 escreveu ovalor 1 em 0 Próximo índice = 1 pool-1-thread-2 escreveu o valor 11 em 0 Próximo índice = 2 pool-1-thread-1 escreveu o valor 2 em 1 Próximo índice = 3 pool-1-thread-2 escreveu o valor 12 em 2 Próximo índice = 4 pool-1-thread-1 escreveu o valor 3 em 3 Próximo índice = 5 pool-1-thread-2 escreveu o valor 13 em 4 Próximo índice = 6 Conteúdo do vetor: [11, 2, 12, 3, 13, 0]
  • 17.
    • Como asthreads executam independentemente e compartilham um vetor, o resultado pode não ser como desejado. • Pela saída anterior, vimos que a thread 2 sobrescreveu o valor recém escrito pela thread 1. • Situações como essa, nas quais não conseguimos garantir que o resultado será coerente e nas quais o resultado dependerá da ordem em que as threads serão escalonadas são chamadas de condições de corrida.
  • 18.
    • O trechode código no qual as diferentes threads acessam as variáveis compartilhadas (possivelmente, modificando-as) é chamado de seção crítica. • Resumindo: o código visto não é seguro para threads. • Para resolver o problema, transformaremos os trechos de acesso às variáveis compartilhadas em operações atômicas, impedindo que mais de uma thread execute tais instruções simultaneamente.
  • 19.
    EXEMPLO • Código doprojeto Multithread2 alterado enviado por e-mail. • Nesse código, fizemos com que o código do método que adiciona um elemento ao SimpleArray seja atômico. Em Java, isso é feito usando a palavra- chave synchronized. • Quando uma thread está executando o corpo de um método synchronized, nenhuma outra pode executá-la antes da primeira terminar sua execução. • A instrução synchronized também pode ser utilizada para trechos de código.
  • 20.
    PRODUTOR-CONSUMIDOR • Um problemaclássico da computação que envolve processos (ou threads) que produzem recursos para serem consumidos por outros. • Existe um buffer compartilhado para armazenamento dos recursos. O produtor precisa parar de produzir quando o buffer estiver cheio e o consumidor não pode consumir se não houver recurso disponível.
  • 21.
    ABORDAGEM 1 • Utilizarum buffer compartilhado com apenas uma posição (classe BufferSemSincronizacao). • Não há garantia sobre a ordem de execução das threads, logo, muitas vezes valores são produzidos com o buffer lotado ou, ainda, consumidos antes da produção, levando a resultados inconsistentes.
  • 22.
    ABORDAGEM 2 • Utilizarum buffer compartilhado com apenas uma posição, mas utilizando ArrayBlockingQueue (classe BufferBloqueante). • A classe ArrayBlockingQueue pertence ao pacote java.util.concurrent, segura para threads e que implementa a interface BlockingQueue (que estende a classe Queue) e declara os métodos put e take (equivalentes bloqueantes dos métodos offer e poll. • put coloca um elemento no fim da fila bloqueando se a mesma estiver cheia.
  • 23.
    • take retiraum elemento do começo da fila bloqueando se a mesma estiver vazia. • O tamanho do ArrayBlockingQueue é passado como argumento para o construtor e não é aumentado dinamicamente.
  • 24.
    ABORDAGEM 3 • Utilizarum buffer compartilhado com apenas uma posição, mas utilizando métodos sincronizados (synchronized). • Utiliza uma variável booleana ocupado que indica se o buffer está totalmente ocupado ou não. • O produtor espera enquanto o buffer estiver lotado utilizando uma chamada ao método wait. Similarmente, o consumidor espera enquanto o buffer estiver vazio. • O método wait faz com que o objeto que o executou libere implicitamente o bloqueio sobre o buffer.
  • 25.
    • O produtor/consumidorsó é liberado de sua espera quando for notificado de que o objeto compartilhado já foi utilizado; isso é feito com a chamada a notifyAll. • O método notifyAll retira do estado de espera todas as threads que estiverem esperando pelo objeto compartilhado, fazendo com que elas passem ao estado executável e tentem readquirir o bloqueio.
  • 26.
    ABORDAGEM 4 • Utilizarum buffer circular compartilhado com 4 posições. • Permite minimizar o tempo de espera das threads devido ao aumento das posições disponíveis para escrita e leitura. • Utiliza: • uma variável auxiliar para controlar quantos elementos válidos realmente há no buffer em um determinado momento. • Dois índices para indicar onde será feita a próxima leitura ou a próxima escrita.
  • 27.
    MULTITHREADING COM GUI •Aplicativos GUI são desafiantes para programação multithreaded. • Aplicativos swing possuem uma única thread, a thread de despacho de eventos, responsável por tratar as interações com os componentes GUI do aplicativo. • Todas as tarefas que exigem interação com a GUI do aplicativo são colocadas em uma fila de eventos e executadas em sequência pela thread de despacho de eventos.
  • 28.
    • Componentes GUISwing não são seguros para threads. • Segurança para threads em aplicativos GUI é obtida assegurando que os componentes Swing são acessado a partir de uma única thread (a de despacho de eventos). Essa técnica é chamada de confinamento de thread. • Utilizando essa ideia, uma alternativa interessante é colocar as atividades de longa duração, que independem da interface enquanto são executadas, em threads separadas da de despacho de eventos.
  • 29.
    • Essa alternativaevita que a thread de despacho de eventos fique sem responder enquanto espera pelo resultado de tais atividades. • Para auxiliar na programação dessas atividades mais demoradas em threads separadas, o Java SE 6 fornece a classe SwingWorker. Essa classe pode realizar cálculos demorados e, então, atualizar s componentes Swing a partir da thread de despacho de eventos com base nos resultados dos cálculos. • SwingWorker implemente a interface Runnable.
  • 30.
    • Métodos comunsde SwingWorker: • doInBackground: define o cálculo longo na thread trabalhadora; • done: executado na thread de despacho de eventos quando doInBackground retorna; • execute: agenda o objeto SwingWorker a ser executado em uma thread trabalhadora; • get: espera a conclusão do cálculo e retorna o seu resultado; • publish: envia resultados intermediários dos cálculos para processamento na thread de despacho de eventos.
  • 31.
    • process: recebeos resultados intermediários e os processa na thread de despacho de eventos; • setProgress: configura a propriedade de progresso para notificar os ouvintes de alteração de propriedade na thread de despacho de eventos de atualizações da barra de progresso.
  • 32.
    EXEMPLO 1: FIBONACCI •No exemplo, forneceremos uma opção para calcular os termos da sequência de Fibonacci um a um ou para calcular um termo específico (até o 92º) em uma thread trabalhadora. • SwingWorker é uma classe genérica que recebe dois parâmetros: o primeiro é o tipo retornado pelo método doInBackground e o segundo é o tipo passado entre os métodos publish e process.
  • 33.
    EXEMPLO 2: ÍMPARES •No exemplo, calculamos todos os números ímpares menores que um valor inteiro fornecido como entrada. • Para obtenção dos ímpares, utilizamos o crivo de Eratóstenes. • Os resultados são exibidos em uma área de texto à medida em que são obtidos. • Uma barra de progresso mostra o quanto do cálculo já foi concluído até um determinado momento.
  • 34.
    REFERÊNCIAS • Java ComoProgramar – 8ª Edição. Deitel & Deitel.