The Codest
  • Sobre nós
  • Serviços
    • Desenvolvimento de software
      • Desenvolvimento de front-end
      • Desenvolvimento backend
    • Staff Augmentation
      • Programadores Frontend
      • Programadores de back-end
      • Engenheiros de dados
      • Engenheiros de nuvem
      • Engenheiros de GQ
      • Outros
    • Aconselhamento
      • Auditoria e consultoria
  • Indústrias
    • Fintech e Banca
    • E-commerce
    • Adtech
    • Tecnologia da saúde
    • Fabrico
    • Logística
    • Automóvel
    • IOT
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • A nossa equipa
  • Case Studies
  • Saber como
    • Blogue
    • Encontros
    • Webinars
    • Recursos
Carreiras Entrar em contacto
  • Sobre nós
  • Serviços
    • Desenvolvimento de software
      • Desenvolvimento de front-end
      • Desenvolvimento backend
    • Staff Augmentation
      • Programadores Frontend
      • Programadores de back-end
      • Engenheiros de dados
      • Engenheiros de nuvem
      • Engenheiros de GQ
      • Outros
    • Aconselhamento
      • Auditoria e consultoria
  • Valor para
    • CEO
    • CTO
    • Gestor de entregas
  • A nossa equipa
  • Case Studies
  • Saber como
    • Blogue
    • Encontros
    • Webinars
    • Recursos
Carreiras Entrar em contacto
Seta para trás VOLTAR
2022-06-15
Desenvolvimento de software

Concorrência em Java Parte 1 - Introdução

The Codest

Rafal Sawicki

Programador Java

Leia a primeira parte da nossa série de blogues dedicada à concorrência em Java. No artigo a seguir, veremos mais de perto as diferenças entre thread e processos, pools de threads, executores e muito mais!

Em geral, a abordagem de programação convencional é sequencial. Tudo num programa acontece um passo de cada vez.
Mas, na verdade, o paralelo é a forma como o mundo inteiro funciona - é a capacidade de executar mais do que uma tarefa em simultâneo.

Linha vs. processo

Para debater temas avançados como concorrência em Java ou multithreading, temos de chegar a acordo sobre algumas definições comuns para termos a certeza de que estamos na mesma página.

Comecemos pelo básico. No mundo não-sequencial, temos dois tipos de representações de concorrência: processos e
threads. Um processo é uma instância do programa em execução. Normalmente, está isolado de outros processos.
O sistema operativo é responsável pela atribuição de recursos a cada processo. Além disso, actua como um condutor que
programa e controla-os.

A thread é uma espécie de processo, mas num nível inferior, pelo que também é conhecida como light thread. Várias threads podem ser executadas numa
processo. Aqui o programa actua como um agendador e um controlador de threads. Desta forma, os programas individuais parecem fazer
várias tarefas ao mesmo tempo.

A diferença fundamental entre threads e processos é o nível de isolamento. O processo tem seu próprio conjunto de
enquanto a thread partilha dados com outras threads. Pode parecer uma abordagem propensa a erros e, de facto, é. Para
Não nos vamos debruçar sobre isso, uma vez que ultrapassa o âmbito deste artigo.

Processos, threads - ok... Mas o que é exatamente a concorrência? Concorrência significa que é possível executar várias tarefas ao mesmo tempo
tempo. Não significa que essas tarefas tenham de ser executadas em simultâneo - é isso que é o paralelismo. Concurrenc em Javay também não
requer que tenha várias CPUs ou mesmo vários núcleos. Pode ser conseguido num ambiente de núcleo único, tirando partido de
mudança de contexto.

Um termo relacionado com a concorrência é o multithreading. Esta é uma caraterística dos programas que lhes permite executar várias tarefas ao mesmo tempo. Nem todos os programas utilizam esta abordagem, mas os que o fazem podem ser designados por multithreaded.

Estamos quase prontos para começar, só mais uma definição. Assincronia significa que um programa executa operações sem bloqueio.
Inicia uma tarefa e depois continua com outras coisas enquanto espera pela resposta. Quando obtém a resposta, pode reagir a ele.

Todo esse jazz

Por defeito, cada Aplicação Java é executado num processo. Nesse processo, existe um thread relacionado com o principal() método de
uma aplicação. No entanto, como já foi referido, é possível tirar partido dos mecanismos de múltiplas threads numa
programa.

Executável

Linha é um Java na qual a magia acontece. Esta é a representação do objeto da thread mencionada anteriormente. Para
criar o seu próprio thread, pode estender o Linha classe. No entanto, esta não é uma abordagem recomendada. Fios deve ser utilizado como um mecanismo para executar a tarefa. As tarefas são partes de código que queremos executar num modo concorrente. Podemos defini-los utilizando a função Executável interface.

Mas chega de teoria, vamos pôr o nosso código onde está a nossa boca.

Problema

Suponhamos que temos um par de matrizes de números. Para cada matriz, queremos saber a soma dos números numa matriz. Vamos
É suposto que existam muitas destas matrizes e que cada uma delas seja relativamente grande. Nessas condições, queremos utilizar a concorrência e somar cada matriz como uma tarefa separada.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Runnable task1 = () -> {
    int soma = Arrays.stream(a1).soma();
    System.out.println("1. A soma é: " + soma);
};

Runnable task2 = () -> {
    int sum = Arrays.stream(a2).sum();
    System.out.println("2. A soma é: " + soma);
};

Runnable task3 = () -> {
    int sum = Arrays.stream(a3).sum();
    System.out.println("3. A soma é: " + soma);
};

nova Thread(task1).start();
nova Thread(tarefa2).start();
new Thread(task3).start();

Como pode ver no código acima Executável é uma interface funcional. Contém um único método abstrato executar()
sem argumentos. O Executável deve ser implementada por qualquer classe cujas instâncias se destinem a ser
executada por um thread.

Depois de definir uma tarefa, pode criar um segmento para a executar. Isto pode ser feito através de novo Thread() construtor que
toma Executável como seu argumento.

O último passo é iniciar() um segmento recentemente criado. Na API existem também executar() métodos em Executável e em
Linha. No entanto, essa não é uma forma de aproveitar a concorrência em Java. Uma chamada direta a cada um destes métodos resulta em
executando a tarefa no mesmo thread que o principal() método é executado.

Grupos de threads e executores

Quando existem muitas tarefas, criar um thread separado para cada uma não é uma boa ideia. Criar uma Linha é um
operação pesada e é muito melhor reutilizar os fios existentes do que criar novos.

Quando um programa cria muitos threads de curta duração, é melhor usar um pool de threads. O pool de threads contém um número de
tópicos prontos para serem executados mas atualmente não activos. Dando um Executável para o pool faz com que uma das threads chame a função
executar() método de um determinado Executável. Após a conclusão de uma tarefa, o segmento continua a existir e está num estado inativo.

Ok, já percebeu - prefira o pool de threads em vez da criação manual. Mas como é que se pode utilizar os pools de threads? O Executores
tem uma série de métodos estáticos de fábrica para construir pools de threads. Por exemplo newCachedThredPool() cria
uma piscina na qual são criadas novas threads conforme necessário e as threads inactivas são mantidas durante 60 segundos. Em contrapartida,
newFixedThreadPool() contém um conjunto fixo de threads, no qual as threads inactivas são mantidas indefinidamente.

Vejamos como pode funcionar no nosso exemplo. Agora não precisamos de criar threads manualmente. Em vez disso, temos de criar
ExecutorService que fornece um conjunto de threads. Em seguida, podemos atribuir-lhe tarefas. O último passo é fechar a thread
para evitar fugas de memória. O resto do código anterior permanece o mesmo.

ExecutorService executor = Executors.newCachedThreadPool();

executor.submit(task1);
executor.submit(task2);
executor.submit(task3);

executor.shutdown();

Acessível

Executável parece ser uma forma elegante de criar tarefas concorrentes, mas tem uma grande falha. Ele não pode retornar nenhum
valor. Além disso, não podemos determinar se uma tarefa está concluída ou não. Também não sabemos se foi concluída
normal ou excecionalmente. A solução para estes males é Acessível.

Acessível é semelhante a Executável de certa forma, também envolve tarefas assíncronas. A principal diferença é que ele é capaz de
devolver um valor. O valor de retorno pode ser de qualquer tipo (não primitivo) como o Acessível é um tipo parametrizado.
Acessível é uma interface funcional que tem chamar() que pode lançar um método Exceção.

Agora vamos ver como podemos aproveitar Acessível no nosso problema da matriz.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Callable task1 = () -> Arrays.stream(a1).sum();
Callable task2 = () -> Arrays.stream(a2).sum();
Callable task3 = () -> Arrays.stream(a3).sum();

ExecutorService executor = Executors.newCachedThreadPool();
Future future1 = executor.submit(task1);
Future future2 = executor.submit(task2);
Future future3 = executor.submit(task3);

System.out.println("1. A soma é: " + future1.get());
System.out.println("2. A soma é: " + future2.get());
System.out.println("3. A soma é: " + future3.get());

executor.shutdown();

Ok, podemos ver como Acessível é criado e depois apresentado ao ExecutorService. Mas o que raio é Futuro?
Futuro actua como uma ponte entre threads. A soma de cada matriz é produzida numa thread separada e precisamos de uma forma de
obter esses resultados de volta para principal().

Para obter o resultado de Futuro precisamos de chamar get() método. Aqui pode acontecer uma de duas coisas. Primeiro, o
resultado do cálculo efectuado por Acessível estiver disponível. Então, recebemo-lo imediatamente. Em segundo lugar, o resultado não é
já está pronto. Neste caso get() irá bloquear até que o resultado esteja disponível.

ComputableFuture

O problema com Futuro é o facto de funcionar segundo o "paradigma push". Ao utilizar Futuro tem de ser como um chefe que
pergunta constantemente: "A sua tarefa está concluída? Está pronta?" até obter um resultado. Atuar sob pressão constante é
caro. Uma abordagem muito melhor seria encomendar Futuro o que fazer quando estiver pronto para a sua tarefa. Infelizmente,
Futuro não pode fazer isso, mas ComputableFuture pode.

ComputableFuture funciona no 'paradigma pull'. Podemos dizer-lhe o que fazer com o resultado quando tiver concluído as suas tarefas. O
é um exemplo de uma abordagem assíncrona.

ComputableFuture funciona perfeitamente com Executável mas não com Acessível. Em vez disso, é possível fornecer uma tarefa ao
ComputableFuture sob a forma de Fornecedor.

Vejamos como o que foi dito acima se relaciona com o nosso problema.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

CompletableFuture.supplyAsync(() -> Arrays.stream(a1).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a2).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a3).sum())
                .thenAccept(System.out::println);

A primeira coisa que nos chama a atenção é o facto de esta solução ser muito mais curta. Para além disso, também tem um aspeto limpo e arrumado.

Tarefa para CompletableFuture pode ser fornecido por supplyAsync() método que recebe Fornecedor ou por runAsync() que
toma Executável. Um retorno de chamada - um pedaço de código que deve ser executado na conclusão da tarefa - é definido por thenAccept()
método.

Conclusões

Java fornece muitas abordagens diferentes para a concorrência. Neste artigo, mal tocámos no assunto.

No entanto, abordámos as noções básicas de Linha, Executável, Acessívele CallableFuture o que coloca uma boa questão
para uma investigação mais aprofundada do tema.

Artigos relacionados

Desenvolvimento de software

9 erros a evitar ao programar em Java

Que erros devem ser evitados ao programar em Java? No artigo seguinte, respondemos a esta pergunta.

The Codest
Rafal Sawicki Programador Java
Soluções para empresas e escalas

A forma correta de encontrar os melhores programadores Java

Encontrar o programador Java perfeito pode ser uma tarefa difícil. Como a procura de tais profissionais no mercado cresce a um ritmo espantoso, as fontes disponíveis para a procura de talentos podem, por vezes, parecer...

The Codest
Grzegorz Rozmus Líder de unidade Java
Soluções para empresas e escalas

10 empresas do Dubai que vale a pena observar em 2020

O Dubai é o coração dos Emirados Árabes Unidos, com o seu mercado cada vez mais próspero de empresas globais e startups promissoras. Muitas podem gabar-se do seu sucesso internacional e de produtos notáveis....

Tuna Pinar
Soluções para empresas e escalas

Como é que Java pode apoiar o seu negócio?

Antes de começarmos, gostaria de vos recordar um aspeto importante. Java não é apenas uma linguagem de programação.

Bartlomiej Kuczynski
Soluções para empresas e escalas

Um dia na vida de um programador no The Codest

Pode pensar-se que os horários de trabalho dos programadores não diferem uns dos outros. Mas isso não é verdade! Cada startup, software house ou mesmo empresa tem o seu próprio horário de...

The Codest
Pawel Rybczynski Software Engineer

Subscreva a nossa base de conhecimentos e mantenha-se atualizado sobre os conhecimentos do sector das TI.

    Sobre nós

    The Codest - Empresa internacional de desenvolvimento de software com centros tecnológicos na Polónia.

    Reino Unido - Sede

    • Office 303B, 182-184 High Street North E6 2JA
      Londres, Inglaterra

    Polónia - Pólos tecnológicos locais

    • Parque de escritórios Fabryczna, Aleja
      Pokoju 18, 31-564 Cracóvia
    • Embaixada do Cérebro, Konstruktorska
      11, 02-673 Varsóvia, Polónia

      The Codest

    • Início
    • Sobre nós
    • Serviços
    • Case Studies
    • Saber como
    • Carreiras
    • Dicionário

      Serviços

    • Aconselhamento
    • Desenvolvimento de software
    • Desenvolvimento backend
    • Desenvolvimento de front-end
    • Staff Augmentation
    • Programadores de back-end
    • Engenheiros de nuvem
    • Engenheiros de dados
    • Outros
    • Engenheiros de GQ

      Recursos

    • Factos e mitos sobre a cooperação com um parceiro externo de desenvolvimento de software
    • Dos EUA para a Europa: Porque é que as empresas americanas decidem mudar-se para a Europa?
    • Comparação dos centros de desenvolvimento da Tech Offshore: Tech Offshore Europa (Polónia), ASEAN (Filipinas), Eurásia (Turquia)
    • Quais são os principais desafios dos CTOs e dos CIOs?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Direitos de autor © 2026 por The Codest. Todos os direitos reservados.

    pt_PTPortuguese
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese es_ESSpanish nl_NLDutch etEstonian elGreek cs_CZCzech pt_PTPortuguese