Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Impara Lock e Condition | Meccanismi di Sincronizzazione di Alto Livello
Quizzes & Challenges
Quizzes
Challenges
/
Multithreading in Java

bookLock e Condition

In Java, la sincronizzazione standard si basa sulla parola chiave synchronized e sugli oggetti monitor integrati. Tuttavia, in alcuni casi, synchronized può non essere sufficiente, specialmente quando è richiesta una maggiore flessibilità nella gestione dei thread.

Descrizione Generale

Le interfacce Lock e Condition, introdotte nel pacchetto java.util.concurrent.locks, offrono funzionalità avanzate per la gestione dei thread.

In questa immagine, si può vedere che il primo thread acquisisce il lock utilizzando il metodo lock(), e in questo momento, un altro thread non può acquisire lo stesso lock. Non appena tutto il codice all'interno del lock viene eseguito, verrà chiamato il metodo unlock() e il lock verrà rilasciato. Solo dopo, il secondo thread potrà acquisire il lock.

Differenza

La differenza tra queste due interfacce è che le implementazioni di Lock rappresentano un'alternativa di alto livello al blocco synchronized, mentre le implementazioni dell'interfaccia Condition sono un'alternativa ai metodi notify()/wait(). Entrambe queste interfacce fanno parte del pacchetto java.util.concurrent.locks.

Esempi nella vita reale

Immagina di gestire una coda per la registrazione a un evento. Per prevenire il sovraffollamento e garantire una corretta assegnazione dei posti, è necessario utilizzare meccanismi di blocco e condizioni per mantenere il flusso delle nuove registrazioni in attesa fino a quando non si libera un posto.

Classe ReentrantLock

La classe ReentrantLock del package java.util.concurrent.locks è un'implementazione dell'interfaccia Lock. Fornisce funzionalità per la gestione esplicita dei lock.

Principali metodi di ReentrantLock:

  • lock(): Acquisisce un lock;
  • unlock(): Rilascia il lock;
  • tryLock(): Tenta di acquisire il lock e restituisce true se l'acquisizione ha successo;
  • tryLock(long timeout, TimeUnit unit): Tenta di acquisire il lock per il tempo specificato;
  • newCondition(): Crea una condizione per il Lock corrente.
Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }

Come puoi vedere, nel metodo increment() utilizziamo il blocco con Lock. Quando un thread entra nel metodo, acquisisce il lock tramite lock.lock(), esegue il codice e poi, nel blocco finally, rilascia il lock con lock.unlock() affinché altri thread possano accedere.

Rilasciamo il lock nel blocco finally per un motivo: questo blocco viene quasi sempre eseguito, anche in presenza di eccezioni, tranne quando il programma viene terminato.

Interfaccia Condition

Possiamo creare una Condition solo associandola a una specifica implementazione di Lock. Per questo motivo, i metodi di Condition influenzeranno solo il blocco di quella particolare implementazione di Lock.

Main.java

Main.java

copy
12
private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();

I principali metodi di Condition:

  • await(): Attende un segnale da un altro thread;
  • signal(): Sblocca un thread in attesa su una condition;
  • signalAll(): Sblocca tutti i thread in attesa sulla condition.
Main.java

Main.java

copy
12345678910111213141516
private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }

Il metodo waitForCondition() blocca il thread fino a quando la variabile ready diventa true, segnalando che la condizione è stata soddisfatta. Quando la condizione è soddisfatta, il thread continua l'esecuzione, mostrando il messaggio “Condizione soddisfatta!”

Note
Nota

Quando il metodo await() viene chiamato, il thread viene messo in pausa e rilascia anche il lock che ha acquisito. Quando il thread si risveglia, deve acquisire nuovamente il lock e solo allora inizierà l'esecuzione!

Esempio di codice

Vediamo ora un esempio di utilizzo di ReentrantLock e Condition per gestire la registrazione a un evento:

Un breve estratto dal video

Blocco con ReentrantLock: Il metodo register() acquisisce il lock con lock.lock() per impedire che più thread eseguano il codice contemporaneamente.

Condition con Condition: Se non ci sono spazi disponibili, il thread invoca spaceAvailable.await() per attendere fino a quando uno spazio diventa disponibile.

Rilascio del lock: Quando un thread ha liberato uno spazio utilizzando il metodo cancel(), richiama spaceAvailable.signalAll() per notificare tutti i thread in attesa.

Gestione delle eccezioni: L'utilizzo dei blocchi try-finally garantisce che il lock venga rilasciato anche in caso di eccezione.

Note
Nota

L'utilizzo di Lock e Condition in Java consente un controllo più flessibile sui thread e sulla sincronizzazione rispetto al tradizionale meccanismo synchronized. Questo risulta particolarmente utile in scenari complessi in cui è necessario un controllo più preciso sui thread e sulle condizioni di attesa.

Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 3. Capitolo 1

Chieda ad AI

expand

Chieda ad AI

ChatGPT

Chieda pure quello che desidera o provi una delle domande suggerite per iniziare la nostra conversazione

Awesome!

Completion rate improved to 3.33

bookLock e Condition

Scorri per mostrare il menu

In Java, la sincronizzazione standard si basa sulla parola chiave synchronized e sugli oggetti monitor integrati. Tuttavia, in alcuni casi, synchronized può non essere sufficiente, specialmente quando è richiesta una maggiore flessibilità nella gestione dei thread.

Descrizione Generale

Le interfacce Lock e Condition, introdotte nel pacchetto java.util.concurrent.locks, offrono funzionalità avanzate per la gestione dei thread.

In questa immagine, si può vedere che il primo thread acquisisce il lock utilizzando il metodo lock(), e in questo momento, un altro thread non può acquisire lo stesso lock. Non appena tutto il codice all'interno del lock viene eseguito, verrà chiamato il metodo unlock() e il lock verrà rilasciato. Solo dopo, il secondo thread potrà acquisire il lock.

Differenza

La differenza tra queste due interfacce è che le implementazioni di Lock rappresentano un'alternativa di alto livello al blocco synchronized, mentre le implementazioni dell'interfaccia Condition sono un'alternativa ai metodi notify()/wait(). Entrambe queste interfacce fanno parte del pacchetto java.util.concurrent.locks.

Esempi nella vita reale

Immagina di gestire una coda per la registrazione a un evento. Per prevenire il sovraffollamento e garantire una corretta assegnazione dei posti, è necessario utilizzare meccanismi di blocco e condizioni per mantenere il flusso delle nuove registrazioni in attesa fino a quando non si libera un posto.

Classe ReentrantLock

La classe ReentrantLock del package java.util.concurrent.locks è un'implementazione dell'interfaccia Lock. Fornisce funzionalità per la gestione esplicita dei lock.

Principali metodi di ReentrantLock:

  • lock(): Acquisisce un lock;
  • unlock(): Rilascia il lock;
  • tryLock(): Tenta di acquisire il lock e restituisce true se l'acquisizione ha successo;
  • tryLock(long timeout, TimeUnit unit): Tenta di acquisire il lock per il tempo specificato;
  • newCondition(): Crea una condizione per il Lock corrente.
Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031
package com.example; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Main { // Creating a ReentrantLock object private final Lock lock = new ReentrantLock(); private int count = 0; // Method to increment the `count` variable public void increment() { lock.lock(); // Acquiring the `lock` try { count++; System.out.println("Count incremented to: " + count); } finally { lock.unlock(); // Releasing the `lock` } } public static void main(String[] args) { Main example = new Main(); // Runnable task to call the increment method Runnable task = example::increment; // Starting multiple threads to execute the task for (int i = 0; i < 5; i++) { new Thread(task).start(); } } }

Come puoi vedere, nel metodo increment() utilizziamo il blocco con Lock. Quando un thread entra nel metodo, acquisisce il lock tramite lock.lock(), esegue il codice e poi, nel blocco finally, rilascia il lock con lock.unlock() affinché altri thread possano accedere.

Rilasciamo il lock nel blocco finally per un motivo: questo blocco viene quasi sempre eseguito, anche in presenza di eccezioni, tranne quando il programma viene terminato.

Interfaccia Condition

Possiamo creare una Condition solo associandola a una specifica implementazione di Lock. Per questo motivo, i metodi di Condition influenzeranno solo il blocco di quella particolare implementazione di Lock.

Main.java

Main.java

copy
12
private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition();

I principali metodi di Condition:

  • await(): Attende un segnale da un altro thread;
  • signal(): Sblocca un thread in attesa su una condition;
  • signalAll(): Sblocca tutti i thread in attesa sulla condition.
Main.java

Main.java

copy
12345678910111213141516
private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean ready = false; public void waitForCondition() throws InterruptedException { lock.lock(); // Acquire the `lock` try { while (!ready) { // Check if the condition is not met System.out.println("Waiting..."); // Print a waiting message condition.await(); // Wait for the condition to be signaled } System.out.println("Condition met!"); // Print a message when the condition is met } finally { lock.unlock(); // Release the `lock` } }

Il metodo waitForCondition() blocca il thread fino a quando la variabile ready diventa true, segnalando che la condizione è stata soddisfatta. Quando la condizione è soddisfatta, il thread continua l'esecuzione, mostrando il messaggio “Condizione soddisfatta!”

Note
Nota

Quando il metodo await() viene chiamato, il thread viene messo in pausa e rilascia anche il lock che ha acquisito. Quando il thread si risveglia, deve acquisire nuovamente il lock e solo allora inizierà l'esecuzione!

Esempio di codice

Vediamo ora un esempio di utilizzo di ReentrantLock e Condition per gestire la registrazione a un evento:

Un breve estratto dal video

Blocco con ReentrantLock: Il metodo register() acquisisce il lock con lock.lock() per impedire che più thread eseguano il codice contemporaneamente.

Condition con Condition: Se non ci sono spazi disponibili, il thread invoca spaceAvailable.await() per attendere fino a quando uno spazio diventa disponibile.

Rilascio del lock: Quando un thread ha liberato uno spazio utilizzando il metodo cancel(), richiama spaceAvailable.signalAll() per notificare tutti i thread in attesa.

Gestione delle eccezioni: L'utilizzo dei blocchi try-finally garantisce che il lock venga rilasciato anche in caso di eccezione.

Note
Nota

L'utilizzo di Lock e Condition in Java consente un controllo più flessibile sui thread e sulla sincronizzazione rispetto al tradizionale meccanismo synchronized. Questo risulta particolarmente utile in scenari complessi in cui è necessario un controllo più preciso sui thread e sulle condizioni di attesa.

Tutto è chiaro?

Come possiamo migliorarlo?

Grazie per i tuoi commenti!

Sezione 3. Capitolo 1
some-alt