Skip to content Skip to footer

Concorrência, OOP e Serverless: Threads, Polimorfismo e o Futuro do Desenvolvimento até 2025

1. Panorama e motivação

O mundo do desenvolvimento de software está cada vez mais exigente: demanda por aplicações escaláveis, com alta concorrência, responsivas e com baixo custo operacional. Nesse cenário:
  • Concorrência e paralelismo são temas centrais para aproveitar os recursos (CPU, threads, I/O) de forma eficiente.
  • A Programação Orientada a Objetos (OOP) continua presente como paradigma prevalente em muitas linguagens e arquiteturas corporativas. Saber como OOP convive (ou conflita) com concorrência é uma necessidade.
  • O surgimento e a maturação do serverless (Functions as a Service, FaaS, arquitetura sem servidor) trazem novas formas de pensar escalabilidade e gerenciamento de recursos, mas também impõem restrições (tempo de execução, latência de cold starts, limitação de paralelismo interno).
Além disso, há previsões otimistas de adoção de serverless nas empresas: estudos indicam que até 2025 cerca de 50 % das empresas poderiam usar serverless de maneira significativa. Há discussões em redes sociais (como no X) sobre como o serverless está a forçar reavaliações do paradigma “thread por requisição” e como as empresas devem adaptar seus modelos de concorrência.

Neste artigo, vamos explorar:


Os conceitos de concorrência, threads e paralelismo.

Relação entre OOP (herança, polimorfismo) e concorrência.

O que serverless muda — desafios e oportunidades de usar threads / paralelismo dentro de funções.

2. Concorrência, paralelismo e threads — conceitos fundamentais

Antes de mais nada, precisamos diferenciar alguns termos frequentemente confundidos:
  • Concorrência (concurrency): capacidade de um sistema gerenciar múltiplas tarefas simultaneamente, mesmo que não necessariamente executando todas ao mesmo tempo. É mais uma questão de estrutura e design (interleaving, agendamento) do que execução paralela real.
  • Paralelismo (parallelism): de fato executar várias tarefas ao mesmo tempo, usando múltiplos núcleos de CPU ou unidades de execução.
  • Um programa concorrente pode ser também paralelo — ou não, dependendo da disponibilidade de hardware.

2.1 Threads e os desafios delas

Uma thread é uma unidade de execução dentro de um processo. Threads compartilham o mesmo espaço de memória e recursos do processo-mãe — isso permite comunicação direta, mas também traz os perigos clássicos:
  • Condições de corrida (race conditions)
  • Deadlocks, starvation
  • Problemas de visibilidade (memory consistency)
  • Sobrecarga de criação, destruição e context switching entre threads.

Um texto clássico “The Problem with Threads” descreve bem como threads, apesar de poderosas, trazem complexidade enorme e são fonte de muitos bugs.

Muitas linguagens — especialmente nas que adotam OOP — têm modelos de threads relativamente robustos (Java, C++, C#). Mas combinar threads com herança, encapsulamento, polimorfismo exige atenção extra.

2.2 Concorrência em OOP: como objetos “pensam concorrência”

Na literatura acadêmica, há classificação de objetos concorrentes:
  • Objetos sequenciais: apenas uma thread ativa por vez
  • Objetos quasi‑concurrentes: múltiplas threads “simuladas”, mas apenas uma ativa num dado momento, via interleaving controlado
  • Objetos concorrentes plenos: objetos que podem manter múltiplas threads internas (cada método pode rodar em paralelo)

Isso mostra que nem toda classe ou objeto está preparado de fábrica para lidar com concorrência — se temos herança, polimorfismo ou delegação, precisamos pensar em:

  • Estado compartilhado entre métodos: se dois métodos de instância acessam a mesma variável mutável, podem conflitar.
  • Sincronização: locks, monitores, semáforos, etc.
  • Atomicidade: operações que parecem “uma linha” podem ser interrompidas no meio.
  • Princípio da menor violência: projetar objetos de forma que sejam, idealmente, imutáveis ou escrevam pouco em estado compartilhado.

Um exemplo prático: imagine uma classe ContaBancaria com métodos deposita(...) e retira(...). Se várias threads chamarem esses métodos simultaneamente, e ambos manipulam o saldo interno sem sincronização, pode haver corrupção de dados.

Também é possível usar polimorfismo de forma segura em contextos concorrentes: há padrões (por exemplo, thread-safe adapters, immutable subclassing, copy-on-write) que ajudam a isolar ou proteger o estado compartilhado.

Na prática, muitos frameworks de concorrência preferem composição em vez de herança — por exemplo, delegar parte concorrente a componentes encapsulados que cuidam do sincronismo, enquanto mantêm o núcleo orientado a objetos “limpo”.

3. Serverless + concorrência interna: o que muda

Com o modelo serverless, muitas premissas tradicionais da execução ficam diferentes:
  • Cada invocação de função geralmente é isolada e efêmera.
  • Funções têm limites estritos de tempo de execução, memória e CPU.
  • Há latência de cold start, ou seja, a preparação inicial da função antes de responder ao evento.
  • Escalabilidade automática horizontal: funções múltiplas para atender múltiplos eventos simultâneos.

Então, surge a pergunta: vale a pena usar threads, paralelismo interno dentro de uma função serverless?

3.1 Por que alguém faria isso (e quando faz sentido)

  • Quando uma operação dentro da função é CPU-bound (cálculo pesado, transformação de dados) ou I/O-bound (acesso concorrente a APIs externas, leitura de múltiplos arquivos).
  • Para evitar latência de rede: se várias sub-tarefas precisam ser feitas, rodá-las em paralelo pode reduzir o tempo total.
  • Para aproveitar alocação de recursos: em plataformas como AWS Lambda, mais memória significa mais CPU/vCPUs — se você conseguir paralelizar trabalho, isso pode trazer ganho. Por exemplo, Lambda hoje permite até 6 vCPUs para funções com bastante memória.

Um exemplo recente mostra como em AWS Lambda com Python, você pode usar multithreading ou multiprocessing para acelerar partes da lógica.

3.2 Os grandes obstáculos e limites

Apesar das motivações, há sérias restrições:

  • Cold starts: instanciar threads ou inicializar paralelismo agrava o overhead de startup.
  • Limitações de tempo máximo de execução nas plataformas (ex: 15 minutos na AWS Lambda).
  • Compartilhamento de estado é mais difícil de coordenar em funções isoladas.
  • Isolamento e escalabilidade: a filosofia de serverless é que cada instância de função deve ser independente e descartável — complica comunicação entre threads “dentro” e “entre” instâncias.
  • Limites de memória / CPU / número de threads permitidas pela plataforma.

Além disso, há uma tensão entre simplicidade (modelo tradicional de uma função = “uma unidade de trabalho”) e a complexidade de multithreading interno. Muitos desenvolvedores argumentam que, se você está fazendo paralelismo complexo, talvez você devesse usar outro modelo (containers, microserviços, VMs) em vez de FaaS.

3.3 Estratégias e padrões para controlar concorrência

Algumas estratégias que surgem como boas práticas no mundo serverless:

  1. Reservar concorrência (Reserved Concurrency)
    Em AWS Lambda, você pode reservar um número máximo de invocações concorrentes para uma função — isso dá controle e evita sobrecarga de downstream.
  2. Mapear estratégias de filas/eventos
    Utilizar filas (SQS, EventBridge, Kafka) para desconectar carga e regular fluxo, evitando “bombardeio” de invocações simultâneas que podem causar throttling.
  3. Batching e particionamento
    Em vez de processar cada evento individualmente, agrupe eventos para reduzir overhead de invocação.
  4. Comunicação entre sub-tarefas via mensagens ou objetos passados
    Não usar memória compartilhada complexa entre threads, mas usar padrões como futures, promises, actor model ou message passing.
  5. Limitar paralelismo interno
    Não fazer paralelismo ilimitado: usar pools, limitar número máximo de threads, adaptabilidade dinâmica.
  6. Funções especializadas para paralelismo pesado
    Para jobs realmente pesados (por exemplo, transformações em lote, ML), use arquiteturas híbridas: serverless apenas para orquestração, funções backend em containers ou clusters.

Também há pesquisas recentes propondo novos esquemas de agendamento e execução para serverless que permitam melhor balanceamento de carga e paralelismo interno. Por exemplo, o trabalho Hiku propõe agendamento pull-based para funções serverless, reduzindo latências e melhorando distribuição de carga sob alta concorrência.

Outro estudo argumenta que o modelo tradicional FaaS não é suficiente para “burst-parallel jobs” (tarefas paralelas massivas) e propõe extensões ou novos primitivas para agrupar invocações sincronizadas.

E estudos como o Hydra propõem runtime multi-língua para consolidar invocações e reduzir overhead de “container tax”.

Portanto, embora as limitações existam, o campo está ativo e evoluindo.

4. Polimorfismo & threads: alguns exemplos e cuidados práticos

Quando misturamos OOP e threads, polimorfismo pode ocorrer de formas interessantes, mas também perigosas. Vamos ilustrar com pseudocódigo (ou estilo similar):

// Classe base
abstract class Tarefa {
    public abstract void executa();
}

// Subclasse
class TarefaA extends Tarefa {
    public void executa() {
        // lógica específica
    }
}

// Em outro lugar...
Tarefa t = new TarefaA();
Thread thr = new Thread(() -> {
    t.executa();
});
thr.start();

Esse padrão simples funciona: o método polimórfico executa() é invocado dentro de uma thread. O perigo surge quando:

  • executa() modifica estado compartilhado que outras threads também acessam.
  • Métodos herdados ou sobrepostos alteram variáveis comuns.

Um caso prático mais avançado envolve thread pools e invocação polimórfica em paralelo. Por exemplo, uma lista de distintos objetos Tarefa pode ser submetida simultaneamente a um ExecutorService em Java; cada objeto pode ter uma lógica própria, mas concorrente.

Ora, o problema clássico surge se esses objetos compartilham (por herança ou composição) um recurso mutável — por exemplo, um contador global, cache, ou estado de sessão. Se não for sincronizado, podemos ter interleaving imprevisível.

Um exemplo de discussão em C++ mostra exatamente esse tipo de padronização de polimorfismo + threading: alguém propôs usar:

derived obj1;
base *p = &obj1;
std::thread t(&base::fun_2, p);

Ou seja, invocar método virtual via ponteiro base dentro de uma thread. Funciona, mas precisa que fun_2 (e o objeto) esteja preparado para ser executado concorrentemente.

Cuidados práticos:

  • Sempre trate variáveis compartilhadas com sincronização ou projetos de imutabilidade.
  • Prefira objetos com estado mínimo ou isolado em cenários concorrentes.
  • Use padrões como thread-safe decorators, copies de objetos, locks finos, estruturas imutáveis.
  • Teste intensivamente sob carga: bugs de concorrência são notórios por só aparecerem em condições extremas (race conditions intermitentes).

5. O que se discute no X / Twitter e previsões para 2025

No mundo tech, há debates acesos sobre o papel de multithreading interno em funções serverless vs o modelo mais “puro” de invocação simples. Algumas correntes defendem:

  • Que funções serverless deveriam ser sempre tratadas como “single-thread” para manter a simplicidade e previsibilidade.
  • Outras defendem que, com plataformas que permitem múltiplos vCPUs (como as novas versões do Lambda), fazer paralelismo interno é uma forma de extrair desempenho extra (mas isso exige maturidade dev).
  • Há quem critique que vamos “reverter” a uma arquitetura de microserviços ou containers quando a lógica ficar complexa demais para uma única função.

Em posts no Reddit/X, aparece a preocupação de que as pessoas “pensam demais em thread único” quando poderiam explorar multi-threading mais agressivamente — mas isso exige domínio de sincronização e limites de recursos.

Quanto a previsões: se um estudo técnico estima que 50 % das empresas integrarão serverless até 2025, vale especular que talvez 75 % das empresas que lidam com workloads de backend escalável adotem ao menos parte do stack em serverless (funções + orquestração) até 2025 — especialmente startups e empresas em nuvem pura. É uma previsão ambiciosa, mas não impossível, considerando que os grandes provedores (AWS, Azure, GCP) seguem investindo pesado em reduzir latência, melhorar escalabilidade e oferecer controles avançados de concorrência.

Também é provável que vejamos:

  • Modelos híbridos (serverless + microsserviços/clusters) continuarem prevalecendo para workloads mais exigentes.
  • Runtime serverless internos que permitem paralelismo mais sofisticado (e menos overhead de containerização).
  • Novos padrões de programação que fazem “serverless + multithreading” de maneira segura “fora da caixa”.

6. Conclusões e boas práticas recomendadas

Em resumo:

  • Concorrência, threads e paralelismo são ingredientes indispensáveis para sistemas escaláveis e responsivos.
  • Integrar essas técnicas de forma segura em um paradigma OOP exige atenção a estado compartilhado, sincronização e padrões de design.
  • No mundo serverless, paralelismo interno faz sentido em casos específicos, mas não é a regra — exige trade‑offs entre performance e complexidade.
  • O ecossistema serverless está evoluindo, com pesquisas ativas propondo novas arquiteturas de execução, agendamento e consolidação de invocações.
  • As previsões são otimistas: muitas empresas integrarão serverless de forma significativa até 2025, e aquelas que dominarem concorrência e paralelismo terão vantagem competitiva.

Boas práticas para adotar agora:

  1. Comece simples: use funções “single-thread” até que você identifique gargalos claros.
  2. Meça perfil de CPU, latência de I/O, tempos de execução, overheads de startup.
  3. Se for adotar paralelismo interno, isole bem o estado mutável ou use técnicas imutáveis.
  4. Use orquestração e filas para distribuir carga em vez de tentar “tudo numa função só”.
  5. Fique de olho nas inovações em runtime serverless, novas versões de funções (ex: suportando múltiplos vCPUs, comunicações entre threads, scheduling híbrido).

Vamos trabalhar juntos
Envia-nos um email - hello@damascode.pt

Damascode – Digital Agency © 2025. All Rights Reserved.