Outils de Concurrence en Java : CountDownLatch, CyclicBarrier et Semaphore

CountDownLatch

CountDownLatch permet à un ou plusieusr threads d'attendre la fin d'un ensemble d'opérations dans d'autres threads. Lors de la création, une valeur de comptage est spécifiée. L'appel à await() bloque le thread jusqu'à ce que le comptage atteigne zéro, ce qui se produit chaque fois que countDown() est invoqué, décrémentant ainsi le compteur. Une fois à zéro, tous les threads en attente sont libérés.

public class ExempleCompteur {
    public static void main(String[] args) throws InterruptedException {
        int nbTaches = 5;
        CountDownLatch verrou = new CountDownLatch(nbTaches);
        for (int index = 0; index < nbTaches; index++) {
            new Thread(() -> {
                System.out.println("Thread " + Thread.currentThread().getId() + " a effectué son travail");
                verrou.countDown();
            }).start();
        }
        verrou.await();
        System.out.println("Toutes les tâches sont complétées.");
    }
}

Résultat attendu :

Thread 10 a effectué son travail
Thread 11 a effectué son travail
Thread 12 a effectué son travail
Thread 13 a effectué son travail
Thread 14 a effectué son travail
Toutes les tâches sont complétées.

CyclicBarrier

CyclicBarrier synchronise un groupe de threads de sorte qu'ils attendent tous d'atteindre un point de barrière commun. Contrairement à CountDownLatch, cette barrière est cyclique et peut être réutilisée après libération des threads. Un constructeur permet de spécifier une action à exécuter lorsque tous les threads ont atteint la barrière.

public class ExempleBarriere {
    public static void main(String[] args) {
        int nombreParticipants = 5;
        CyclicBarrier barriere = new CyclicBarrier(nombreParticipants, () -> {
            System.out.println("La barrière a été franchie par " + nombreParticipants + " threads.");
        });
        for (int idx = 0; idx < nombreParticipants; idx++) {
            new Thread(() -> {
                System.out.println("Thread " + Thread.currentThread().getId() + " arrive à la barrière.");
                try {
                    barriere.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Résultat attendu :

Thread 10 arrive à la barrière.
Thread 11 arrive à la barrière.
Thread 12 arrive à la barrière.
Thread 13 arrive à la barrière.
Thread 14 arrive à la barrière.
La barrière a été franchie par 5 threads.

Semaphore

Un sémaphore à comptage contrôle l'accès à une ressource en limitant le nombre de threads simultenés. Il maintient un ensemble de permis : acquire() bloque le thread jusqu'à ce qu'un permis soit disponible, tandis que release() libère un permis, permettant à d'autres threads de procéder.

public class ExempleSemaphore {
    public static void main(String[] args) {
        int maxAccesConcurrents = 3;
        Semaphore semaphore = new Semaphore(maxAccesConcurrents);
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getId() + " accède à la ressource.");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getId() + " libère la ressource.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            }).start();
        }
    }
}

Résultat attendu :

10 accède à la ressource.
11 accède à la ressource.
12 accède à la ressource.
11 libère la ressource.
10 libère la ressource.
12 libère la ressource.
13 accède à la ressource.
...

Étiquettes: Java Concurrency CountDownLatch CyclicBarrier Semaphore

Publié le 10 juillet à 07h01