Aller au contenu principal

Verrous

  1. Verrou implicite (synchronisation via synchronized)
    Le mot-clé synchronized est la méthode la plus simple pour verrouiller une section de code. Il permet de s'assurer qu'un seul thread accède à un bloc de code ou à une méthode à la fois.

    Exemple :

    public class Compteur {
    private int count = 0;

    // Méthode synchronisée pour éviter les conditions de course
    public synchronized void increment() {
    count++;
    }

    public synchronized int getCount() {
    return count;
    }
    }

    Pourquoi l'utiliser ?
    Ce verrou est utile pour garantir que les modifications sur une ressource partagée (par exemple, une variable) ne soient pas effectuées simultanément par plusieurs threads. Cela permet de prévenir les incohérences.

  2. Verrou explicite (Lock et ReentrantLock)
    ReentrantLock est une classe de la bibliothèque java.util.concurrent.locks qui offre plus de contrôle et de flexibilité par rapport à synchronized. Contrairement à synchronized, qui verrouille une méthode ou un bloc de code entier, ReentrantLock permet de verrouiller manuellement une section de code et de déverrouiller explicitement le verrou à l'aide de méthodes comme lock() et unlock().

    Exemple :

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    public class Compteur {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
    lock.lock(); // Acquisition du verrou
    try {
    count++;
    } finally {
    lock.unlock(); // Libération du verrou
    }
    }

    public int getCount() {
    lock.lock();
    try {
    return count;
    } finally {
    lock.unlock();
    }
    }
    }

    Pourquoi l'utiliser ?
    Ce verrou est préféré dans des scénarios où il est nécessaire de contrôler le verrouillage de manière plus fine, par exemple, pour utiliser des fonctionnalités comme tryLock() ou pour implémenter des mécanismes plus complexes de gestion des verrouillages.

  3. Verrou ReadWriteLock ReadWriteLock permet de séparer les verrous en deux types : un verrou pour les lectures et un verrou pour les écritures. Cela permet de permettre à plusieurs threads de lire simultanément une ressource partagée tout en interdisant l'accès en écriture pendant la lecture, et inversement.

    Exemple avec ReentrantReadWriteLock :

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;

    public class Compteur {
    private int count = 0;
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void increment() {
    Lock writeLock = rwLock.writeLock();
    writeLock.lock();
    try {
    count++;
    } finally {
    writeLock.unlock();
    }
    }

    public int getCount() {
    Lock readLock = rwLock.readLock();
    readLock.lock();
    try {
    return count;
    } finally {
    readLock.unlock();
    }
    }
    }

    Pourquoi l'utiliser ?
    Ce verrou est particulièrement utile lorsque les ressources partagées sont fréquemment lues et rarement écrites. Il permet de maximiser les performances en autorisant l'accès concurrent aux ressources en lecture tout en excluant les écritures simultanées.

Comparaison

  • synchronized est simple et direct, mais il est limité dans ses capacités (pas de contrôle explicite sur l'acquisition et la libération du verrou, pas de temps d'attente ou de tentatives de verrouillage).

  • ReentrantLock offre plus de flexibilité et permet des fonctionnalités avancées comme tryLock(), lockInterruptibly(), ou même des verrouillages temporisés.

  • ReadWriteLock est parfait lorsque l'accès en lecture est beaucoup plus fréquent que l'accès en écriture, ce qui permet d'optimiser les performances en cas de lecture concurrente.