ProgrammazioneJava middle/backend developer

Cosa sai del lavoro con i thread in Java e come organizzare la sicurezza dei thread?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

In Java, il lavoro con i thread è realizzato attraverso le classi Thread, Runnable e dal pacchetto java.util.concurrent. Per organizzare la sicurezza dei thread vengono utilizzati diversi meccanismi di sincronizzazione:

  • Blocchi/metodi sincronizzati (synchronized) consentono a più thread di accedere a dati condivisi senza gare.
  • volatile garantisce la visibilità delle modifiche della variabile tra i thread.
  • Classi di java.util.concurrent (ad esempio, ReentrantLock, Semaphore, AtomicInteger, ConcurrentHashMap) offrono modi di sincronizzazione più flessibili.

Esempio di sincronizzazione:

public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }

Esempio con uso di classi atomiche:

import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }

Domanda trabocchetto.

Garantisce la parola chiave volatile la sicurezza dei thread durante l'incremento di un contatore di tipo int?

Risposta: No. volatile garantisce solo la visibilità del valore tra i thread, ma non l'atomicità delle operazioni. L'incremento non è un'operazione atomica (count++ consiste in lettura, incremento e scrittura), quindi è possibile la perdita di dati. Per un incremento thread-safe sono necessari sincronizzazione o classi come AtomicInteger.

volatile int count = 0; // count++ non è thread-safe!

Esempi di errori reali a causa della mancanza di conoscenza delle sottigliezze del tema.


Storia

In un negozio online, il conto bonus veniva aggiornato tramite un contatore volatile. Sotto carico, migliaia di utenti ordinavano prodotti e parte dei bonus andava persa a causa di gare sui thread su count++.


Storia

Un dipendente utilizzava un normale ArrayList come buffer comune tra produttore e consumatore in un'applicazione multithreading, risultando in ConcurrentModificationException. La soluzione è stata l'uso di blocchi synchronized o la sostituzione con CopyOnWriteArrayList.


Storia

Nella gestione dei pagamenti, un sviluppatore effettuava l'operazione "controllare e addebitare l'importo" tramite due metodi non correlati. In condizioni di carico elevato ciò portava a doppie addebiti fino a che non sono state implementate transazioni atomiche e bloccaggi.