Filosofia de Aplicações Concorrentes

Postado em Atualizado em

Ao longo das experiências que venho tendo com as consultorias, treinamentos, equipes de desenvolvimento, certificações etc..percebo que no geral, a galera apresenta uma grande dificuldade relacionado com o tratamento de questões relacionadas a Thread. Após algumas conversas e entrevistas, diagnostiquei que o fato acontece não por causa da complexidade da API ou dificuldades de sintaxe, mas por falta de uma efetivo entendimento da FILOSOFIA sobre a questão. Eu peguei uma série de casos aonde o pessoal (no geral) não conseguia dominar ou entender sintaxe/API justamente pela fato de não ter uma clara instrução do propósito e fundamento da mesma. Com isso, resolvi criar este post com o intuito de cobrir esse buraco e tentar esclarecer o assunto:

O objetivo de toda equipe de desenvolvimento é produzir programas que executem uma série de tarefas logicamente organizadas. Na maioria dos casos é isso mesmo, simples assim….um sistema programado para executar uma monte de tarefas seqüências e cronologicamente estipulada. Por uma outra perspectiva, o sistema somente executara “uma tarefa por vez”. A questão surge quando é identificado que em algumas partes de alguns programas, existem certos processos que não necessitam ser executados seqüencialmente, ou seja, não faz sentido para aquele escopo em especial que uma tarefa espere uma outra ser terminada para que a mesma possa ser executada. Podemos pegar o exemplo clássico de  um editor de texto – não faz sentido que o usuário deva esperar o arquivo de 100 páginas ser impresso para depois salva-lo. Na verdade, o programa deveria permitir que o usuário pudesse fazer estas duas tarefas ao mesmo tempo. Com isso vemos que podemos ter 2 tipos de aplicação:

  • Monothread – que executa tarefas seqüencias.
  • Multithread – que executa tarefas paralelas, ao mesmo tempo e concorrentemente.

É por isso que dizemos a tecnologia java é multithread….isso quer dizer que com ela podemos criar programas que executem inúmeras tarefas concorrentemente. O autor do programa java esta livre para criar e delimitar quantos processos seqüencias e concorrentes poderão acontecer dentro do sistema. Esta mágica acontece simplesmente com a utilização da interface Runnable e a classe Thread. Que legal, que lindo..maravilhoso ? Não..nada de lindo e maravilhoso !! A possibilidade de fazer aplicações multithread resulta em uma situação aonde muitos problemas podem surgir. Vejamos os comuns:

Acesso Concorrente
Se vc para e pensar que um programa pode executar inúmeras tarefas ao mesmo tempo, chegara a conclusão que poderá acontecer situações aonde dois ou mais processos poderão manipular os mesmos recursos causando o problema chamado de acesso concorrente. Eu sempre costumo dizer a POO nada mais é que o reflexo das coisas que acontece no mundo real e isso não é diferente para a questão do acesso concorrente. Imagine a situação aonde um pai tente gerenciar que os seus dois filhos brinquem com seu brinquedos. Do ponto de vista funcional a coisa é simples, ou o pai bloqueia uma criança enquanto a outra brinca com o brinquedo ou ele compra um brinquedo para cada criança, fazendo com que elas possam brincar ao mesmo tempo.
Por causa desta situação é que encontramos o controle de bloqueio e o modificador chamado synchronized na tecnologia java. Com ele, o desenvolvedor tem a capacidade de bloquear um objeto, impedindo que o mesmo seje usando por um outro(s) processo(s) que porventura tente acessa-lo. Veja o oposto é verdadeiro,… a ausência do operador faz que com o objeto possar ser utilizado concorrentemente, ficando então a cargo do desenvolvedor identificar os casos e decidir oque fazer. Um caso clássico exemplo apresentando pela maioria dos livros seria duas pessoas tentando sacar dinheiro de uma mesma conta bancária. Caso os objetos responsáveis pelas operações não seje corretamente bloqueado, poderá finalizar em estado inconsistente.

Gerenciamento de Threads
Estamos nesta conversa toda justamente pelo fato de algumas processos não precisarem ser executados seqüencialmente, concluindo que a programação multithread foi concebida para deixar os sistemas mais rápidos e interativos. A coordenação das threads acontece em um terceiro cenário aonde um sistema multithread apresenta casos em que determinados processos concorrentes precisam ser controlados minuciosamente, apresentando uma certa interdependência entre eles. Imagine uma situação onde cinco crianças querendo comprar sorvete, possuindo apenas um atendente para todas elas. Vamos usar este simples exemplo e levantar algumas variações das situações:

  1. A criança que for selecionada pelo atendente, terá que bloqueá-lo fazendo com que ele não atenda uma outra criança em paralelo. A solução para esta exemplo de controle de acesso já foi mencionado anteriormente utilizando o operador synchronized.
  2. Se caso uma criança selecionada para ser atendida ainda estiver em dúvida qual sabor da casquinha escolher, o oposto terá que acontecer, ou seja, o sorveteiro terá que ser liberado para que possar escolher uma outra criança para atender, enquanto aquela que teve a oportunidade fica um tempo de espera indeterminado até que tenha uma nova oportunidade.
  3. Se caso uma criança selecionada para ser atendida falar assim “Eu quero o mesmo sabor que  meu irmãozinho mais velho”. Este é o caso onde o sorveteiro terá que ser liberado para que possar escolher uma outra criança e somente voltar nesta depois do seu irmão for atendido.
  4. Se caso uma criança selecionada para ser atendida falar assim “Eu gostaria de passar a minha vez para minha irmãzinha mais nova que esta morrendo de vontade de tomar sorvete”. Este é o caso conde o sorveteiro terá que ser liberado e forçado a atender uma determinada criança em específico.

Se no mundo real acontece assim, em java não sera diferente ! Uma equipe de desenvolvimento pode de deparar com um escopo de projeto multithread que contenha algum(s) caso(s) de gerenciamento de thread e a tecnologia contém todos os artifícios necessários para que o desenvolvedores possam implementar este controle. Para os casos 2, 3, e 4 acima descritos, vemos que os métodos herdados da classe Object: wait(), notify() e notifyAll() são utilizados para implementar este controle sobre as instancias do objetos que estam sendo executados dentro das threads. Juntamente com eles, podemos destacar os métodos das classe classe Thread: join(), resume() isAlive(), yield(), interrupted(), setDaemon() e sleep() que também são usados dar a possibilidade dos desenvolvedores implementarem o controle das threads dentro deste ambiente gerenciável.

Outros casos mais “cabeludos” que eu deixarei de fora para não aumentar o post seria o DeadLock, Race Conditions e o Starvation. Veja os link para mais detalhes.

Bom é isso galera, o post não tem o objetivo de descrever cada caso ou detalhamentos sobre as API da tecnologia java para ambientes multithread, ficando a carga de cada um se aprofundar no assunto. Para os interessados, segue dois livros que podem fornecer o conteúdo necessários – Certificação Sun para Programador Java 5 – Inglês e Português e SCJD Exam with J2SE 5 Inglês. O post fica aberto para opiniões e complementos 🙂

“Instruirei e te ensinarei o caminho  que deves seguir; e, sob as minhas vistas, te darei direcionamento.” Salmos 32:8

3 comentários em “Filosofia de Aplicações Concorrentes

    Gustavo Meyer disse:
    26/02/2009 às 11:41

    Parabéns, pelo post, ficou muito bom a abordagem.

    Gostei do ideia do sorveteiro isso me mostrou uma forma diferente de pensar.

    Obrigado.

    William G. Comnisky disse:
    19/09/2010 às 13:37

    Saudações,

    Gostaria de saber onde encontro referência sobre a palavra-chave synchronized ser um modificador de acesso.

    grato

      Fernando Franzini disse:
      20/09/2010 às 11:43

      Realmente “synchronized” é chamado de modificador, mas não de acesso.
      Já corrigi o texto.
      Obrigado pela dica.
      A paz.

Deixe um comentário