Este artigo apresenta os principais recursos da linguagem Java para o desenvolvimento de aplicações com linhas de execução concorrentes, abrangendo sincronização de sessões críticas, criação de linhas de execução adicionais com threads, o uso de classes auxiliares, entre outros.
Tais recursos são acessíveis sob uma sintaxe simplificada e moderna, sem perder em eficiência nem legibilidade.
É apresentada também uma aplicação exemplo multi-thread que utiliza muitos dos recursos de concorrência, incluindo a implementação de semáforos disponibilizada pela linguagem Java. Em aplicações práticas, a correta sincronização de programas concorrentes pode evitar problemas sérios e difíceis de serem detectados.
Para exemplificar a aplicação de soluções possíveis para problemas de sincronização, é proposta uma aplicação exemplo baseada numa interface gráfica que utiliza tais funcionalidades para garantir a sincronia no acesso a recursos compartilhados.
Vamos abordar nesse artigo as principais classes e comandos da linguagem Java para se trabalhar com linhas de execução adicionais pertencentes ao mesmo programa, que são conhecidas como threads.
Cada aplicação Java já contém um threads por padrão, que é o thread principal da própria aplicação, porém outras linhas de execução adicionais podem ser criadas para realizar tarefas simultâneas.
Um exemplo de aplicação multi-thread clássico é um servidor de chat, onde vários clientes podem se conectar a ele usando sockets, causando a realização de diversas tarefas simultaneamente, como criação de novas conexões, fechamento de conexões existentes, gerenciamento de interface gráfica, realização de backup, envio e recebimento de mensagens para cada cliente e outros.
Usando threads, evitamos o caso de paralisar o servidor toda vez que uma dessas requisições precisar ser atendida, pois cada uma dessas tarefas é realizada ao mesmo tempo.
No dia a dia é comum usar aplicações que envolvam threads, como aplicações com interfaces gráficas (GUIs) e aplicações de comunicação. Em tais aplicações são executadas diversas tarefas simultaneamente por linhas de execução paralelas, onde o sistema operacional designa threads aos processadores durante intervalos de tempo que ele mesmo determina.
Felizmente, Java oferece recursos de alto nível que escondem a verdadeira complexidade do tratamento de threads no nível do sistema operacional e do hardware, permitindo ao desenvolvedor se focar no desenvolvimento da correta sincronização do uso de recursos compartilhados por threads através do uso de comandos simples e eficazes.
Um problema que pode prejudicar aplicações multi-thread se não tratado corretamente é a sincronização do acesso a recursos compartilhados, mais comumente caracterizados na forma de variáveis e objetos.
Portanto, em muitas vezes, organizações são prejudicadas por utilizarem software que contém falhas no controle de acesso a recursos compartilhados, levando a problemas como o caso da aplicação tentar criar registros no banco de dados com violação de unicidade de chaves únicas, perda de dados, erros esporádicos na aplicação, lentidão excessiva do sistema, entre outros.
Na maioria das linguagens de programação, não é uma atividade trivial desenvolver um programa multi-thread que apresenta sincronização eficiente de acesso a recursos compartilhados por tais threads, porém, na linguagem Java, são disponibilizadas uma série de classes, comandos e métodos a fim de facilitar o controle de acesso a tais recursos, liberando o programador de precisar vincular fortemente a lógica da aplicação ao controle de threads, aumentando a legibilidade do código-fonte, além de sua eficiência.
Disputa de sessões críticas
Um problema muito comum que pode ocorrer na programação concorrente é a atualização de uma variável compartilhada entre vários threads. Na Listagem 1 considere o trecho de código apresentado como sendo uma sessão crítica, ou seja, como sendo um trecho de código com variáveis que são visíveis e acessíveis para mais de um threads.
x = x + y; y = x + y + 1;
Se considerarmos que a sessão crítica na listagem é executada somente por um único thread, sempre será obtido o resultado correto ao final da sessão crítica. Como exemplo, levando-se em conta que x e y iniciam com o valor 1, o resultado será x=2 e y=4 ao final da linha 2.
Além disso, podemos considerar outro exemplo em que dois threads distintos, (a) e (b), estão a ponto de executar a sessão crítica e o sistema operacional, praticamente ao mesmo tempo, concede ao pedido de ambos os threads para executar essa sessão crítica.
Nessa situação, levando-se em conta o caso de haver um único processador, enquanto um thread estiver executando a sessão crítica, ele pode perder o uso do processador para o outro threads, podendo assim levar a resultados finais incorretos, já que ambos os threads compartilham ambas as variáveis.
O mesmo problema ocorre em computadores com múltiplos processadores ou núcleos, pois não se pode garantir a ordem de intercalação na execução das operações de threads concorrentes. Considere o caso ótimo em que o threads (a) ganha acesso à sessão crítica e a completa e logo em seguida o threads (b) ganha acesso à sessão crítica e a completa, como na Listagem 2.
(a) x = x + y; // (x=2, y=1) (a) y = x + y + 1; // (x=2, y=4) : resultado para (a) (b) x = x + y; // (x=6, y=4) (b) y = x + y + 1; // (x=6, y=11) : resultado para (b)
Nessa listagem, podemos verificar que o threads (a) executou a sessão crítica e obteve o resultado x=2 e y=4 (linha 2), e o threads (b) executou o mesmo código e obteve o resultado x=6 e y=11 (linha 4), que são os resultados corretos para cada thread.
Considere agora a possibilidade em que o sistema operacional realizou a intercalação das mesmas operações em uma ordem diferente, como exemplificado na Listagem 3.
(a) x = x + y; // (x=2, y=1) (b) x = x + y; // (x=3, y=1) : resultado para (a) (b) y = x + y + 1; // (x=3, y=5) (a) y = x + y + 1; // (x=3, y=9) : resultado para (b)
Como podemos verificar, o thread(a) executou a sessão crítica e obteve o resultado x=3 e y=9 (linha 4), e o threads (b) executou o mesmo código e obteve o resultado x=6 e y=5 (linha 3), diferindo dos resultados obtidos na Listagem 2.
Uma vez que o sistema operacional não tem como garantir a ordem de execução das operações intercaladas, confirmamos que a ordem de execução das operações pelo sistema operacional é importante para definir o resultado final das operações.
Para evitar tal problema, precisamos de mecanismos que nos façam garantir a execução sincronizada, como na Listagem 2, que leva a resultados corretos, onde um threads executa a sessão crítica por vez. Tal situação é comumente referenciada como “disputa” (race), momento em que dois threads disputam uma sessão crítica, e a solução é fazer com que somente um threads acesse por vez a sessão crítica, ou seja, garantir a exclusão mútua. Se o outro tentar acessar a sessão crítica nesse intervalo de tempo, ficará bloqueado até o primeiro thread terminar.
O referido problema não ocorre somente com variáveis simples, mas sim com todo o tipo de recurso computacional, como com vetores, sockets, impressoras, recursos do sistema operacional e outros.
Felizmente, Java oferece comandos simples para realizar a sincronização de sessões críticas, como o comando synchronized e suas várias aplicações. ...
Confira outros conteúdos:
Black November
Desconto exclusivo para as primeiras 200 matrículas!
Pagamento integral
12x no cartão
De: R$ 69,00
Por: R$ 59,00
Total: R$ 708,00
Garanta o desconto
- Formação FullStack Completa
- Carreira Front-end I e II, Algoritmo e Javascript, Back-end e Mobile
- +10.000 exercícios gamificados
- +50 projetos reais
- Comunidade com + 200 mil alunos
- Estude pelo Aplicativo (Android e iOS)
- Suporte online
- 12 meses de acesso
Pagamento facilitado
Cobrado mensalmente no cartão
De: R$ 79,00
Por: R$ 64,00 /mês
Tempo mínimo: 12 messes
Garanta o desconto
- Formação FullStack Completa
- Carreira Front-end I e II, Algoritmo e Javascript, Back-end e Mobile
- +10.000 exercícios gamificados
- +50 projetos reais
- Comunidade com + 200 mil alunos
- Estude pelo Aplicativo (Android e iOS)
- Suporte online
- Não compromete o limite do seu cartão
<Perguntas frequentes>
Nossos casos de sucesso
Eu sabia pouquíssimas coisas de programação antes de começar a estudar com vocês, fui me especializando em várias áreas e ferramentas que tinham na plataforma, e com essa bagagem consegui um estágio logo no início do meu primeiro período na faculdade.
Estudo aqui na Dev desde o meio do ano passado!
Nesse período a Dev me ajudou a crescer muito aqui no trampo.
Fui o primeiro desenvolvedor contratado pela minha
empresa. Hoje eu lidero um time de desenvolvimento!
Minha meta é continuar estudando e praticando para ser um
Full-Stack Dev!
Economizei 3 meses para assinar a plataforma e sendo sincero valeu muito a pena, pois a plataforma é bem intuitiva e muuuuito didática a metodologia de ensino. Sinto que estou EVOLUINDO a cada dia. Muito obrigado!
Nossa! Plataforma maravilhosa. To amando o curso de desenvolvimento front-end, tinha coisas que eu ainda não tinha visto. A didática é do jeito que qualquer pessoa consegue aprender. Sério, to apaixonado, adorando demais.
Adquiri o curso de vocês e logo percebi que são os melhores do Brasil. É um passo a passo incrível. Só não aprende quem não quer. Foi o melhor investimento da minha vida!
Foi um dos melhores investimentos que já fiz na vida e tenho aprendido bastante com a plataforma. Vocês estão fazendo parte da minha jornada nesse mundo da programação, irei assinar meu contrato como programador graças a plataforma.
Wanderson Oliveira
Comprei a assinatura tem uma semana, aprendi mais do que 4 meses estudando outros cursos. Exercícios práticos que não tem como não aprender, estão de parabéns!
Obrigado DevMedia, nunca presenciei uma plataforma de ensino tão presente na vida acadêmica de seus alunos, parabéns!
Eduardo Dorneles
Aprendi React na plataforma da DevMedia há cerca de 1 ano e meio... Hoje estou há 1 ano empregado trabalhando 100% com React!
Adauto Junior
Já fiz alguns cursos na área e nenhum é tão bom quanto o de vocês. Estou aprendendo muito, muito obrigado por existirem. Estão de parabéns... Espero um dia conseguir um emprego na área.
Utilizamos cookies para fornecer uma melhor experiência para nossos usuários, consulte nossa política de privacidade.