ProgrammazioneBackend Developer

Come funziona il meccanismo synchronized in Java? Quando usarlo e quali insidie ci sono nella sincronizzazione dell'accesso alle risorse?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Storia della questione:

Con l'arrivo di Java, gli sviluppatori si sono trovati di fronte al problema dell'accesso concorrente a risorse condivise. Per affrontare questo problema, sono stati introdotti primitivi di sincronizzazione di alto livello, il principale dei quali è il modificatore chiave synchronized.

Problema:

Senza sincronizzazione, nelle applicazioni multithread, le risorse condivise possono essere danneggiate: si verifica una data race, e lo stato dell'oggetto diventa imprevedibile.

Soluzione:

synchronized consente di organizzare un monitor per un metodo o un blocco di codice, garantendo l'accesso solo a un thread alla volta nella sezione critica. La sincronizzazione del thread può essere implementata a livello di metodo o di blocco.

Esempio di blocco di un oggetto:

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

I blocchi possono anche essere sincronizzati:

public void safeIncrement() { synchronized(this) { count++; } }

Caratteristiche principali:

  • La sincronizzazione a livello di metodo è equivalente alla sincronizzazione a livello di this
  • Per i metodi statici, la sincronizzazione è a livello di classe-oggetto
  • È necessario minimizzare l'area della sezione critica

Domande insidiose.

Qual è la differenza tra un metodo synchronized e un blocco synchronized?

Un metodo synchronized blocca l'intero metodo per l'oggetto corrente (this) o per la classe (se il metodo è statico). Un blocco consente di sincronizzare solo la parte di codice necessaria e di scegliere qualsiasi oggetto per il blocco.

Possono due thread diversi entrare contemporaneamente in due metodi synchronized diversi dello stesso oggetto?

No, se i metodi sono sincronizzati sullo stesso monitor (this). Se utilizzano monitor diversi, allora sì.

Il modificatore synchronized influisce sulla visibilità delle variabili tra i thread?

Sì, l'ingresso in un blocco synchronized resetta le cache dei thread e aggiorna i valori delle variabili (happens-before relationship).

Errori tipici e anti-pattern

  • Sincronizzazione su un oggetto troppo "ampio" (ad esempio, su String o sull'oggetto di classe)
  • Esecuzione prolungata del codice all'interno di un blocco sincronizzato
  • Mischiare l'accesso a una risorsa attraverso blocchi synchronized e sezioni non sincronizzate

Esempio dalla vita reale

Caso negativo

Uno sviluppatore sincronizza metodi statici di una classe su un oggetto istanza, il che non garantisce correttezza quando si utilizza attraverso diverse istanze.

Pro:

  • Implementazione semplice

Contro:

  • Gare di dati inaspettate tra i thread
  • Bug difficile da individuare

Caso positivo

Tutti i metodi che utilizzano una risorsa condivisa sono sincronizzati su un unico oggetto-monitor, l'area della sezione critica è minima.

Pro:

  • I thread lavorano con dati consistenti
  • Minima bloccatura

Contro:

  • Più difficile mantenere e analizzare i blocchi reciproci (deadlock), specialmente con un gran numero di oggetti sincronizzati