<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: João Antônio</title>
    <description>The latest articles on DEV Community by João Antônio (@antjjoao).</description>
    <link>https://dev.to/antjjoao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3578101%2F152e06d8-6369-4bbe-96c3-8bea4efb4bf2.jpeg</url>
      <title>DEV Community: João Antônio</title>
      <link>https://dev.to/antjjoao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antjjoao"/>
    <language>en</language>
    <item>
      <title>Como criar tarefas concorrentes no Java, feito por quem se preocupa com os recursos e performance</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Tue, 01 Sep 2026 01:50:52 +0000</pubDate>
      <link>https://dev.to/antjjoao/como-criar-tarefas-concorrentes-no-java-feito-por-quem-se-preocupa-com-os-recursos-e-performance-4phb</link>
      <guid>https://dev.to/antjjoao/como-criar-tarefas-concorrentes-no-java-feito-por-quem-se-preocupa-com-os-recursos-e-performance-4phb</guid>
      <description>&lt;p&gt;Este post nasce de uma preocupação que tenho levado cada vez mais a sério: entender como cada parte do código realmente impacta o uso de recursos.&lt;/p&gt;

&lt;p&gt;Também tenho usado IA como ferramenta de estudo, principalmente para conseguir acompanhar tanta coisa nova sendo lançada sem perder o foco no que realmente quero aprender.&lt;/p&gt;

&lt;p&gt;Este e outros textos que venho escrevendo também servem como um compromisso comigo mesmo: continuar estudando, escrevendo e tentando entender as coisas de verdade, em vez de apenas consumir informação de forma passiva. No fim, é uma tentativa de não perder esse contato racional com o aprendizado em um mundo onde tudo está fácil demais de acessar e onde é muito fácil começar o dia já sobrecarregando o córtex pré-frontal com dezenas de assuntos diferentes.&lt;/p&gt;

&lt;p&gt;Então vou aqui apenas fomentar o assunto, e você se dispõe a continuar procurando caso queira, variações de implementação, interpretação ou até mesmo contrapor o que eu estou propondo aqui.&lt;/p&gt;

&lt;p&gt;Então vamos lá, o primeiro ponto é entender como o projeto Loom no Java trouxe maturidade pro ciclo de vida de recursos de unidades de processamento, traduzindo, colocou threads controladas pela JVM muito mais leves onde você não precisa passar o controle para o kernel em IO's e deixar a thread dormindo esperando que a volta no socket de rede acorde sua thread para continuar rodando.&lt;/p&gt;

&lt;p&gt;Como esse é um estudo deliberado que acontece após o expediente de trabalho e praticamente no último mês dedico meu tempo a entender todas as motivações e decisões de design da linguagem Java, tenho flertado muito com a programação funcional dado os estudos de números primos e logaritmo discreto, e algumas coisas que me preocuparam foi a dificuldade de criar vínculos de tarefas paralelas no Java, muitos só criam um pool e executam em paralelo, mas e se eu precisar vincular uma tarefa a outra de forma paralela, e se eu precisar cancelar e parar a outra também e não onerar recursos ou segurar recursos, como objetos parados na heap sem coleta, pool de conexão de banco segurando mais que o necessário, chamadas para outros serviços que poderiam ser abortadas (aqui vale estudar RPC, remote procedure call, para cancelamento de tarefas em sistemas distribuídos), exemplo de um código rudimentar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;antifraude&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transacao&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Autorizacao&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A falhou? B continua rodando.&lt;/p&gt;

&lt;p&gt;Agora imagine que você quer executar algumas tarefas em paralelo e que elas formem uma unidade só, ou todas servem ou nenhuma serve, que é exatamente o caso de uma autorização de cartão, onde antes de responder aprovado ou negado você precisa do score de antifraude, da checagem de limite na conta e da consulta de token na bandeira, cada uma vindo de um lugar diferente e todas dentro de uma janela de resposta apertada, porque a bandeira não vai esperar você para sempre. Para não ficar no achismo eu montei um cenário e medi, com o antifraude levando 300ms e retornando certo, a bandeira levando 250ms e retornando certo, e o autorizador de limite falhando aos 50ms. Aos 50ms a resposta já está condenada, essa transação vai ser negada de qualquer jeito, e a pergunta que interessa é o que acontece nos 250ms seguintes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 50 ms] LIMITE falha (a exceção fica guardada calada dentro do Future)
[300 ms] ANTIFRAUDE termina, trabalho jogado fora
[300 ms] BANDEIRA termina, trabalho jogado fora
[300 ms] só agora o get() lança
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deu 600 conexão·ms consumidos para produzir uma negativa que já era certa aos 50ms. E o motivo não é o &lt;code&gt;get()&lt;/code&gt; ser lento, é que ele bloqueia na ordem em que você escreveu e não na ordem em que as coisas acontecem, mas indo mais fundo o problema real é que o pool é um saco de tarefas anônimas, quando você faz submit aquilo vira um Runnable numa fila e o pool não sabe que as três tarefas são irmãs, não sabe que uma depende da outra fazer sentido, não existe a informação de parentesco em lugar nenhum, então não existe ninguém para avisar ninguém.&lt;/p&gt;

&lt;p&gt;E sob carga isso não degrada de forma linear, porque quando as conexões acabam as autorizações começam a enfileirar, a latência sobe, o adquirente faz retry, o que gera mais carga ainda, e um serviço degradado acaba derrubando o autorizador inteiro não por causa dele mas porque você continuou pagando pedágio por trabalho que já estava perdido. Em pagamento isso tem um agravante que eu acho pouco discutido, porque cada chamada que você deixa correr depois da decisão já tomada é uma chamada que pode ter efeito do outro lado, e aí você entra no território de idempotência e reversão.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que a StructuredTaskScope adiciona
&lt;/h2&gt;

&lt;p&gt;É exatamente esse parentesco que a &lt;code&gt;StructuredTaskScope&lt;/code&gt; traz, e sinceramente é só isso que ela traz, o resto é consequência:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Joiner&lt;/span&gt;&lt;span class="o"&gt;.&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;allSuccessfulOrThrow&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTimeout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;))))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fraude&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;antifraude&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transacao&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conta&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;portador&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bandeira&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transacao&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Autorizacao&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fraude&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;limite&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;portador&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O escopo guarda a lista das subtarefas, e quando uma delas termina ele consulta a política, se falhou ele interrompe as irmãs na própria thread que acabou de falhar, no instante da falha, sem polling e sem espera. E o fechamento de chave faz o close(), que manda o cancelamento e ainda espera todas encerrarem de fato antes de deixar você sair do bloco. Aquele withTimeout de 2 segundos também é do conjunto inteiro e não de cada chamada, que é o que você quer quando existe uma janela de autorização a respeitar.&lt;/p&gt;

&lt;p&gt;A analogia que me fez entender de vez foi pensar que concorrência com pool é goto, você lança a tarefa e ela some, a estrutura do código deixa de corresponder à estrutura da execução. O try-with-resources já tinha resolvido isso para recursos, amarrando o tempo de vida ao escopo léxico, e a structured concurrency faz a mesma coisa com threads. O teste que eu passei a aplicar é perguntar quando aquela thread morre, e com pool a resposta honesta é que eu não sei, tenho que ler o código todo e rezar, enquanto com escopo eu aponto com o dedo para o fechamento de chave.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que ela não faz, que é onde eu mais aprendi
&lt;/h2&gt;

&lt;p&gt;Aqui está a parte que mudou como eu penso o assunto, porque cancelar uma tarefa  não significa parar a thread. ninguem mata thread, em linguagem nenhuma.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;Thread.stop()&lt;/code&gt; existiu, foi depreciado em 1998 e desativado de vez no Java 20, e o motivo é bem fundamental, porque uma exceção assíncrona pode nascer entre duas instruções quaisquer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;contaOrigem&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;saldo&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
     &lt;span class="c1"&gt;// morreu aqui&lt;/span&gt;
&lt;span class="n"&gt;contaDestino&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;saldo&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cem reais deixaram de existir e nenhum código consegue se proteger disso, porque toda linha que você escreve assume que a próxima vai rodar, e essa é a suposição mais básica que existe na programação. Não é limitação do Java, o Go tem context.Context, o C# tem CancellationToken, e todos são cooperativos.&lt;/p&gt;

&lt;p&gt;O que existe de verdade é o &lt;code&gt;interrupt()&lt;/code&gt;, que liga um booleano e dá um empurrão em quem está dormindo, ou seja é um bilhete deixado na mesa. Quem está esperando IO acorda, lê o bilhete e se mata sozinho lançando a InterruptedException, e quem está num laço de CPU sem checar nada simplesmente ignora e roda para sempre.&lt;/p&gt;

&lt;p&gt;E aí mora a pegadinha que me pegou. Esse código aqui parece virtuoso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;tentativa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;tentativa&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;tentativa&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;consultarBandeira&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transacao&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;          &lt;span class="c1"&gt;// InterruptedException também é uma Exception&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"falhou, tentando de novo"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;É retry, todo mundo faz, e em integração com bandeira ou adquirente é praticamente obrigatório, ninguém olha isso e vê bug. Só que a JVM limpa a flag antes de lançar a InterruptedException, então a tentativa número 2 começa com a thread limpa, abre uma conexão nova e trabalha o tempo inteiro, porque não sobrou nenhum vestígio de que alguém pediu cancelamento. O sinal foi destruído dentro do catch.&lt;/p&gt;

&lt;p&gt;Medi isso mantendo a coordenação idêntica e trocando só o serviço:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 55 ms] LIMITE falha, cancelamento disparado
[ 57 ms] MÉTODO SAIU em 56 ms, do meu lado tudo parecia certo
[ 59 ms] BANDEIRA engoliu InterruptedException, vai tentar de novo
[ 60 ms] ANTIFRAUDE engoliu InterruptedException, vai tentar de novo
[309 ms] conexão BANDEIRA-retry#2 devolvida (250 ms)
[361 ms] conexão ANTIFRAUDE-retry#2 devolvida (301 ms)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Foram 708 conexão·ms contra 243, e repare que o método retornou aos 56ms com toda a aparência de sucesso, sem log de erro, sem exceção, sem métrica nenhuma. Você só vai descobrir isso quando o pool esgotar sob carga, e vai passar o dia procurando no lugar errado.&lt;/p&gt;

&lt;p&gt;Com o escopo, esse mesmo bug faz o método travar até os 366ms. Parece pior, e é justamente esse o ponto. O bug não sumiu, nada faz um catch mal escrito desaparecer, mas ele deixou de ser uma thread fantasma que ninguém enxerga e virou uma latência com endereço, dentro de um bloco que eu consigo apontar, num thread dump que mostra a árvore de pai e filho. A StructuredTaskScope não te dá poder de matar nada, ela te dá detecção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Onde isso me deixou
&lt;/h2&gt;

&lt;p&gt;A regra que ficou pra mim é que quando você captura uma InterruptedException você tomou posse do único pedido de cancelamento que existia no sistema inteiro, então ou você age ou você devolve com &lt;code&gt;Thread.currentThread().interrupt()&lt;/code&gt;, e um catch vazio não é ignorar, é destruir.&lt;/p&gt;

&lt;p&gt;E vale uma ressalva, a structured concurrency ainda é preview, está no sexto no Java 26 com finalização esperada pro 27, e ela não substitui nada da defesa em camadas que já existia, timeout no recurso com setQueryTimeout e timeout de HTTP, bulkhead separando o pool de cada parceiro, circuit breaker, propagação de deadline. Se o seu autorizador esgota conexão em produção a primeira pergunta certa não é se você usou escopo, é se tem timeout no recurso.&lt;/p&gt;

&lt;p&gt;e obrigado por quem leu até aqui.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>java</category>
    </item>
    <item>
      <title>Cell-Based Architecture: o por que estamos sempre tentando mitigar riscos e falhas</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Tue, 28 Apr 2026 01:21:56 +0000</pubDate>
      <link>https://dev.to/antjjoao/cell-based-architecture-o-porque-estamos-sempre-tentando-mitigar-riscos-e-falhas-21em</link>
      <guid>https://dev.to/antjjoao/cell-based-architecture-o-porque-estamos-sempre-tentando-mitigar-riscos-e-falhas-21em</guid>
      <description>&lt;p&gt;Escalar microserviços horizontalmente resolve capacidade. Mas não                                                                                                                  resolve isolamento, e essa diferença importa mais do que parece.&lt;/p&gt;

&lt;p&gt;Cell-Based Architecture é um padrão que ainda é pouco discutido&lt;br&gt;
na comunidade, mas que empresas como AWS, Slack já usam&lt;br&gt;
há anos para garantir que uma falha nunca afete todos os usuários                                                                                                                ao mesmo tempo.                                                                                                                                                                    &lt;/p&gt;

&lt;p&gt;A ideia é simples: em vez de escalar instâncias de um mesmo sistema,                                                                                                               você replica a stack inteira em unidades independentes chamadas cells, onde cada uma serve um subconjunto da sua base de usuários.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema com microserviços tradicionais
&lt;/h2&gt;

&lt;p&gt;Quando escalamos microserviços horizontalmente, normalmente fazemos isso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64era8w0v5qf3546f4u5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64era8w0v5qf3546f4u5.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O design parece resiliente. Mas na prática, um único ponto de&lt;br&gt;
  pressão afeta todos os usuários ao mesmo tempo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um bug em deployment? Todo mundo cai.&lt;/li&gt;
&lt;li&gt;Um cliente com tráfego absurdo? Degrada a experiência de todos
os outros, o famoso &lt;em&gt;noisy neighbor&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;Uma falha em cascata? Propaga pelo sistema inteiro.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Escala horizontal resolve capacidade. Não resolve isolamento.&lt;br&gt;&lt;br&gt;
  E essa diferença importa muito mais do que parece.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é uma Cell
&lt;/h2&gt;

&lt;p&gt;Uma &lt;strong&gt;cell&lt;/strong&gt; é uma unidade autônoma que contém tudo que precisa&lt;br&gt;
  para servir um subconjunto de usuários. Aplicação, infraestrutura&lt;br&gt;
  e banco de dados juntos, completamente isolados das outras cells. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1ieedwu6cf3f7ddv6cv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1ieedwu6cf3f7ddv6cv.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada cell é funcionalmente idêntica às outras. A diferença é que&lt;br&gt;&lt;br&gt;
  cada uma serve um pedaço diferente da base de usuários e não sabe&lt;br&gt;
  que as outras existem. &lt;/p&gt;

&lt;h2&gt;
  
  
  As quatro propriedades que importam
&lt;/h2&gt;

&lt;p&gt;### 1. Blast Radius Containment&lt;/p&gt;

&lt;p&gt;Se a Cell 2 falha, apenas os usuários entre 1M e 2M são afetados.&lt;br&gt;&lt;br&gt;
  Os outros continuam operando normalmente. O raio da explosão é&lt;br&gt;
  previsível e limitado antes mesmo do incidente acontecer.                                                                                                                          &lt;/p&gt;

&lt;p&gt;### 2. Noisy Neighbor Isolation&lt;/p&gt;

&lt;p&gt;Um cliente corporativo com milhões de requests por dia não consegue&lt;br&gt;
  degradar a experiência de outros clientes que estão em cells separadas.&lt;br&gt;
  Cada um vive no seu próprio ambiente.                                                                                                                                              &lt;/p&gt;

&lt;p&gt;### 3. Canary deployment natural                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;Você faz deploy na Cell 1, monitora por 30 minutos e só então&lt;br&gt;&lt;br&gt;
  propaga para as demais. Sem feature flags complexos. O rollback&lt;br&gt;
  é cirúrgico e afeta só quem precisa.&lt;/p&gt;

&lt;p&gt;### 4. Compliance e isolamento por região&lt;/p&gt;

&lt;p&gt;Precisa garantir que dados de clientes europeus fiquem na Europa?&lt;br&gt;&lt;br&gt;
  Cells por região resolvem isso de forma estrutural, não como&lt;br&gt;
  um patch em cima de outro patch.     &lt;/p&gt;

&lt;h2&gt;
  
  
  Como o roteamento funciona
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;Cell Router&lt;/strong&gt; é o único componente verdadeiramente global.&lt;br&gt;
  Ele resolve para qual cell cada request deve ir:&lt;/p&gt;

&lt;p&gt;Request chega&lt;br&gt;
       |&lt;br&gt;
  Cell Router consulta: qual cell serve esse tenant_id?&lt;br&gt;&lt;br&gt;
       |&lt;br&gt;&lt;br&gt;
  Encaminha para Cell N                                                                                                                                                              &lt;/p&gt;

&lt;p&gt;O mapeamento de &lt;code&gt;tenant_id&lt;/code&gt; para &lt;code&gt;cell_id&lt;/code&gt; fica em um store leve&lt;br&gt;
  e altamente disponível. Redis ou DynamoDB funcionam bem aqui ou outro que queira.                                                                                                                      &lt;/p&gt;

&lt;p&gt;## "Mas isso não é só sharding?"                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;Não exatamente, e essa é a confusão mais comum.                                                                                                                                    &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharding&lt;/strong&gt; é uma estratégia de banco de dados. Você distribui&lt;br&gt;
  os dados entre múltiplos nós, mas a aplicação continua sendo uma só.&lt;/p&gt;

&lt;p&gt;Mas nem tudo nesse modelo representa o melhor dos mundos. Existem partes difíceis nessas decisões arquiteturais.&lt;/p&gt;

&lt;p&gt;Por exemplo, caso seja necessário agregar dados de todas as cells, será preciso consultar e processar informações distribuídas em múltiplos bancos. Isso pode aumentar a complexidade das consultas, dos pipelines de dados e da consistência das informações.&lt;/p&gt;

&lt;p&gt;Outro ponto importante é a sobrecarga de uma cell específica. Se uma cell começar a receber mais tráfego do que o esperado, migrar usuários ou aumentar sua capacidade sem downtime pode ser bem mais complicado. Como os bancos são separados, a redistribuição de dados exige mais cuidado, planejamento e mecanismos seguros de migração.&lt;/p&gt;

&lt;p&gt;Além disso, qualquer mudança estrutural no banco, como alterações de schema, índices ou procedures, precisa ser aplicada em todos os N bancos existentes. Isso exige uma boa estratégia de versionamento, automação e rollout gradual.&lt;/p&gt;

&lt;p&gt;Ou seja, apesar de ser uma solução robusta e escalável, ela também traz novos desafios operacionais. Toda decisão arquitetural resolve alguns problemas, mas inevitavelmente adiciona outros.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>microservices</category>
      <category>systemdesign</category>
      <category>backend</category>
    </item>
    <item>
      <title>por quê?</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Wed, 04 Feb 2026 10:47:10 +0000</pubDate>
      <link>https://dev.to/antjjoao/por-que-4a77</link>
      <guid>https://dev.to/antjjoao/por-que-4a77</guid>
      <description>&lt;p&gt;O desenvolvimento de software exige, de certa forma, um esforço "cognitivo" mínimo da máquina. Diante disso, acredito que pontos como entendimento e compreensão profunda produzem soluções cada vez mais robustas para a grande gambiarra que é a combinação de estruturas lógicas que se transformam em instruções com um determinado fim. O comportamento de uma instrução é interpretado com um nível específico de precisão, não de significado.&lt;/p&gt;

&lt;p&gt;Essa grande gambiarra nasce da interpretação de sinais elétricos. Enquanto elétrons percorrem circuitos em busca de uma fuga para despejar sua energia em algum ponto final, eles se tornam prisioneiros do próprio circuito. São quase escravos de portas que permitem ou bloqueiam sua passagem, velocistas que correm sem saber para onde, sem linha de chegada, queimando todo o combustível disponível até a dissipação total.&lt;/p&gt;

&lt;p&gt;Compreender essas abstrações, assim como as interpretações existentes em cada camada de software e hardware, traz uma enorme sabedoria para a tomada de decisão. Esse entendimento nos conduz à percepção de que tudo depende de contratos fortes e de implementações feitas com o mínimo possível de brechas de sentido e de processo. Quanto mais profundo é o entendimento das camadas, menor é o espaço para ambiguidades e maior é a confiabilidade dos sistemas construídos.&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>JVM, Java Memory Model e CPU: por que funciona em x86 e quebra em ARM</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Mon, 15 Dec 2025 02:17:33 +0000</pubDate>
      <link>https://dev.to/antjjoao/jvm-java-memory-model-e-cpu-por-que-funciona-em-x86-e-quebra-em-arm-280g</link>
      <guid>https://dev.to/antjjoao/jvm-java-memory-model-e-cpu-por-que-funciona-em-x86-e-quebra-em-arm-280g</guid>
      <description>&lt;p&gt;“Mas nunca deu problema na minha máquina.”&lt;/p&gt;

&lt;p&gt;Provavelmente porque sua máquina é x86. Troca pra ARM e alguns bugs de concorrência que estavam “dormindo” aparecem.&lt;/p&gt;

&lt;p&gt;A ideia central: &lt;strong&gt;x86 costuma ser mais “conservador” na prática&lt;/strong&gt;, enquanto &lt;strong&gt;ARM permite mais reordenações&lt;/strong&gt;. Se seu código depende de comportamento “bonzinho” do hardware, ele pode sobreviver em x86 e falhar em ARM.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que o Java Memory Model (JMM) realmente garante
&lt;/h2&gt;

&lt;p&gt;O JMM não fala “threads compartilham memória e pronto”. Ele define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;visibilidade&lt;/strong&gt;: quando o que uma thread escreveu passa a ser visto por outra&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ordem&lt;/strong&gt;: quais reordenações são permitidas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;happens-before&lt;/strong&gt;: a relação que cria garantias reais entre threads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sem um &lt;em&gt;happens-before&lt;/em&gt; entre duas ações, você não tem garantia de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ver o valor mais recente&lt;/li&gt;
&lt;li&gt;ver as coisas na ordem que você escreveu&lt;/li&gt;
&lt;li&gt;ver um objeto “pronto”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso é o motivo de muitos bugs “fantasma”.&lt;/p&gt;




&lt;h2&gt;
  
  
  O erro comum: confiar na ordem do código
&lt;/h2&gt;

&lt;p&gt;Você escreve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E imagina que outra thread, ao ver &lt;code&gt;ready == true&lt;/code&gt;, vai necessariamente ver &lt;code&gt;x == 42&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O JMM permite que, &lt;strong&gt;sem sincronização&lt;/strong&gt;, outra thread observe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ready == true
x == 0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porque:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x pode ficar em cache/registrador&lt;/li&gt;
&lt;li&gt;as escritas podem ser reordenadas&lt;/li&gt;
&lt;li&gt;a outra thread pode ler valores antigos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O clássico pesado: “objeto meio construído”
&lt;/h2&gt;

&lt;p&gt;Esse é o bug mais traiçoeiro.&lt;/p&gt;

&lt;p&gt;instance = new MyObject();&lt;/p&gt;

&lt;p&gt;Isso parece uma operação, mas por baixo vira algo tipo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;alocar memória&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;inicializar campos (construtor)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;publicar a referência (instance aponta pro objeto)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sem sincronização, a JVM/CPU podem efetivamente permitir que a publicação (3) aconteça antes da inicialização (2) ser visível para outras threads.&lt;/p&gt;

&lt;p&gt;Então uma segunda thread pode ver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (instance != null) {
    instance.doSomething();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E instance não é null, mas o objeto pode estar com campos ainda em estado “default” (0/null). Em padrões como double-checked locking isso já deu dor real em produção.&lt;/p&gt;

&lt;h1&gt;
  
  
  Onde volatile entra (e por que resolve)
&lt;/h1&gt;

&lt;p&gt;Se você declara:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static volatile MyObject instance;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você ganha duas coisas importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visibilidade: leitura de volatile vê o valor mais recente (não “preso” em cache local).&lt;/li&gt;
&lt;li&gt;Ordem: volatile cria barreiras que impedem certas reordenações ao redor da variável.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Regra que importa:
&lt;/h1&gt;

&lt;p&gt;Uma escrita em um volatile acontece-before de qualquer leitura posterior do mesmo volatile.&lt;/p&gt;

&lt;p&gt;Na prática: se a thread B leu instance (volatile) como não-nulo, ela passa a ter garantia de enxergar as escritas que aconteceram antes da thread A publicar essa referência (incluindo os writes do construtor que “prepararam” o objeto).&lt;/p&gt;

&lt;h2&gt;
  
  
  “Como funciona por baixo dos panos”
&lt;/h2&gt;

&lt;p&gt;Escrita em volatile&lt;/p&gt;

&lt;p&gt;Quando a JVM compila uma escrita em volatile, ela precisa garantir que:&lt;/p&gt;

&lt;p&gt;todas as escritas anteriores não fiquem “penduradas” e invisíveis&lt;/p&gt;

&lt;p&gt;a publicação do valor não “passe na frente” de coisas anteriores&lt;/p&gt;

&lt;p&gt;Isso é implementado inserindo memory fences/barriers ao redor do acesso (o tipo exato depende da arquitetura e do JIT).&lt;/p&gt;

&lt;p&gt;Leitura de volatile&lt;/p&gt;

&lt;p&gt;Quando a JVM compila uma leitura de volatile, ela precisa garantir que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;você não leia um valor velho do cache/registrador&lt;/li&gt;
&lt;li&gt;leituras seguintes não sejam movidas “pra antes” dessa leitura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Também usa barreiras e instruções com semântica apropriada.&lt;/p&gt;

&lt;h2&gt;
  
  
  x86 vs ARM: por que a diferença aparece
&lt;/h2&gt;

&lt;h1&gt;
  
  
  x86 (modelo mais forte na prática)
&lt;/h1&gt;

&lt;p&gt;Em x86, muitas reordenações são menos agressivas e a plataforma tende a “ajudar” sem você pedir. Não significa que o código está correto — significa que o bug pode não se manifestar.&lt;/p&gt;

&lt;p&gt;Resultado: muita gente escreve código sem happens-before e “passa”.&lt;/p&gt;

&lt;h1&gt;
  
  
  ARM (modelo mais fraco)
&lt;/h1&gt;

&lt;p&gt;ARM permite mais reordenação e exige sincronização explícita para garantir ordem/visibilidade. Se você não usou volatile/synchronized/locks/atomics, ARM tem mais chance de mostrar o bug.&lt;/p&gt;

&lt;p&gt;Resultado: o mesmo programa “ok” em x86 pode falhar em ARM sob carga.&lt;/p&gt;

&lt;p&gt;Regra prática (sem filosofia)&lt;/p&gt;

&lt;p&gt;Se tem compartilhamento entre threads, escolha uma estratégia:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;volatile: bom para flags/estado simples e publicação segura de referência&lt;/li&gt;
&lt;li&gt;&lt;p&gt;synchronized/Lock: bom para invariantes e operações compostas&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Atomic*: bom para operações atômicas sem lock (CAS), com custos/limites próprios&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você não tem happens-before, você está apostando na arquitetura e no acaso.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>java</category>
    </item>
    <item>
      <title>Implementando preço baseado em volumetria com desconto retroativo</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Sat, 08 Nov 2025 14:22:56 +0000</pubDate>
      <link>https://dev.to/antjjoao/implementando-preco-baseado-em-volumetria-com-desconto-retroativo-5505</link>
      <guid>https://dev.to/antjjoao/implementando-preco-baseado-em-volumetria-com-desconto-retroativo-5505</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpdiqmhepns8mec23oo7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpdiqmhepns8mec23oo7.png" alt="twitter post" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos à ideia de trabalhar com tiers por volumetria.&lt;br&gt;
Neste exemplo, defini 3 tiers, onde os valores mínimo e máximo são inclusivos, ou seja, tanto o limite mínimo quanto o limite máximo estão dentro do tier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0418xoapgphd67bbp1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0418xoapgphd67bbp1s.png" alt="structs" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se o max for 0, quer dizer que vai do min até o “infinito”.&lt;br&gt;
Cada tier tem um unitPrice, que é o preço de cada pagamento naquela faixa.&lt;/p&gt;

&lt;p&gt;No PlanVersions, a ideia é ter várias versões de um mesmo plano convivendo ao mesmo tempo.&lt;/p&gt;

&lt;p&gt;Se rolar uma atualização de preço e você não quiser que os clientes antigos sejam impactados, o EffectiveAt entra justamente pra isso: você usa a data pra decidir qual versão do plano vale pra cada cobrança.&lt;/p&gt;

&lt;p&gt;Já o CustomerPricing é o vínculo entre o plano e o cliente — e ali você pode colocar um desconto se o cliente for especial… ou só muito gente boa mesmo kkkkk 😄&lt;/p&gt;

&lt;p&gt;aqui abaixo está o uso/exemplo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7jxyzb7gmg2y3oj7adf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7jxyzb7gmg2y3oj7adf.png" alt=" " width="757" height="744"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;keep going until you're satisfied or dead.&lt;/p&gt;

</description>
      <category>go</category>
      <category>braziliandevs</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>equals, hashcode, hashmap</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Wed, 22 Oct 2025 01:18:13 +0000</pubDate>
      <link>https://dev.to/antjjoao/equals-hashcode-hashmap-5ah2</link>
      <guid>https://dev.to/antjjoao/equals-hashcode-hashmap-5ah2</guid>
      <description>&lt;h2&gt;
  
  
  O papel de equals() e hashCode() no Java
&lt;/h2&gt;

&lt;p&gt;Esses dois métodos vêm da classe Object, a base de todas as classes em Java.&lt;/p&gt;

&lt;p&gt;Eles determinam como o Java compara objetos e como eles são organizados em coleções baseadas em hash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;equals(Object o)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define se dois objetos são considerados “iguais”.&lt;/p&gt;

&lt;p&gt;Por padrão (em Object), equals() compara referências (endereço na memória).&lt;/p&gt;

&lt;p&gt;Classes costumam sobrescrever para comparar conteúdo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Cpu {
    String process;
    Pessoa(String process) { this.process = process; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; 
        if (!(o instanceof Pessoa)) 
        Pessoa p = (Pessoa) o;
        return process.equals(p.process);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hashCode()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retorna um número inteiro usado pelo algoritmo de hashing de coleções como HashMap e HashSet.&lt;/p&gt;

&lt;p&gt;Serve para descobrir rapidamente onde o objeto deve ficar (qual bucket).&lt;/p&gt;

&lt;p&gt;Objetos “iguais” segundo equals() devem ter o mesmo hashCode(),&lt;br&gt;
o contrário não é obrigatório (mesmo hash não garante igualdade).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Override
public int hashCode() {
    return Objects.hash(nome);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Como o HashMap realmente usa isso
&lt;/h2&gt;

&lt;p&gt;Vamos entender o fluxo interno simplificado:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Você chama map.put(chave, valor).&lt;/li&gt;
&lt;li&gt;O Java calcula hash = chave.hashCode().&lt;/li&gt;
&lt;li&gt;Esse hash é usado pra determinar qual “bucket” no array interno vai armazenar a entrada.&lt;/li&gt;
&lt;li&gt;Dentro do bucket, se houver colisões (outros elementos com mesmo hash), ele compara com equals() para decidir se atualiza o valor ou adiciona outro nó.&lt;/li&gt;
&lt;li&gt;Quando você faz map.get(chave), ele repete o processo:

&lt;ul&gt;
&lt;li&gt;calcula o mesmo hashCode(),&lt;/li&gt;
&lt;li&gt;vai ao mesmo bucket,&lt;/li&gt;
&lt;li&gt;percorre e compara com equals() até achar a chave correspondente.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  O que acontece quando a chave muda
&lt;/h2&gt;

&lt;p&gt;Se você altera qualquer campo da chave depois de adicioná-la ao mapa, o hashCode() calculado na inserção não corresponde mais ao hash atual.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O get() procura no bucket errado, não encontra.&lt;/li&gt;
&lt;li&gt;O remove() também falha (não acha a chave antiga).&lt;/li&gt;
&lt;li&gt;A entrada continua existindo dentro de um bucket “órfão”.&lt;/li&gt;
&lt;li&gt;Isso não é um “vazamento” clássico, mas é um objeto perdido logicamente — ocupa memória, mas não é acessível.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exemplo problemático&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Product{
    String name;
    int code;

    Product(String name, int code) {
        this.name= name;
        this.code= code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Product)) return false;
        Produto p = (Product) o;
        return code == p.code;
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora veja o uso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Map&amp;lt;Produto, Double&amp;gt; precos = new HashMap&amp;lt;&amp;gt;();
Produto p = new Produto("Mouse", 101);
precos.put(p, 79.9);


p.codigo = 999;

// Agora o hash mudou e o mapa não encntra mais a chave
System.out.println(precos.get(p)); //  null
System.out.println(precos.size()); //  1 (ainda existe internamente)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O objeto continua dentro do mapa, mas está “preso” em um bucket diferente e inacessível.&lt;/p&gt;

&lt;h2&gt;
  
  
  O contrato formal (equals / hashCode contract)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A especificação do Java diz:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Se dois objetos são iguais segundo equals(), eles devem retornar o mesmo valor em hashCode().&lt;/p&gt;

&lt;p&gt;Se dois objetos têm hashCode() diferentes, eles não podem ser iguais.&lt;/p&gt;

&lt;p&gt;Se dois objetos têm o mesmo hashCode(), eles podem ou não ser iguais (colisão).&lt;/p&gt;

&lt;p&gt;Portanto:&lt;/p&gt;

&lt;p&gt;equals() → true ⇒ hashCode() igual&lt;br&gt;
hashCode() igual ⇒ equals() pode ser true ou false&lt;/p&gt;

&lt;p&gt;feito por João Antônio e caso encontre algum erro, adicione aqui um comentário ou me mande um e-mail.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>java</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
