Un sémaphore en Java est une aide à la synchronisation qui restreint le nombre de threads pouvant accéder à une ressource partagée à un moment donné. Il fait partie du package java.util.concurrent et est utilisé pour gérer l'accès simultané aux ressources, telles que les fichiers, les bases de données ou les connexions réseau.
Un sémaphore contrôle l'accès à un nombre défini de permis. Chaque permis représente le droit d’accéder à une ressource particulière. Le sémaphore garde une trace du nombre de permis disponibles, ce qui détermine le nombre de threads pouvant accéder simultanément à la ressource.
Permis : Un jeton ou un ticket qui permet à un fil de poursuivre l'accès à une ressource partagée.
Lorsque vous créez un sémaphore, vous précisez le nombre de permis disponibles. Ce nombre définit le nombre de threads pouvant accéder simultanément à la ressource.
Avant qu'un thread puisse accéder à la ressource, il doit acquérir un permis du sémaphore. Cela se fait en utilisant la méthode acquire().
Acquire : Cette méthode est appelée lorsqu'un thread souhaite accéder à la ressource. Si un permis est disponible, le sémaphore décrémente le nombre de permis disponibles et permet au thread de continuer. Si aucun permis n'est disponible, le thread est bloqué jusqu'à ce qu'un permis soit disponible.
Comportement de blocage : Si aucune autorisation n'est disponible, le thread qui appelle acquire() sera bloqué (c'est-à-dire qu'il attendra) jusqu'à ce qu'un autre thread libère une autorisation.
Une fois qu'un thread a fini d'utiliser la ressource, il doit libérer le permis pour la rendre disponible pour d'autres threads. Cela se fait en utilisant la méthode release().
Release : Cette méthode incrémente le nombre de permis disponibles. S'il y a des threads en attente d'un permis, l'un d'eux sera débloqué et autorisé à acquérir le permis.
Il existe deux types de sémaphores en Java :
Pour mieux comprendre le fonctionnement des sémaphores, examinons une implémentation pratique. Nous allons créer un scénario simple dans lequel plusieurs threads tentent d'accéder à une ressource limitée.
import java.util.concurrent.Semaphore; public class SemaphoreDemo { // Creating a semaphore with 3 permits private static final Semaphore semaphore = new Semaphore(3); public static void main(String[] args) { // Creating and starting 6 threads for (int i = 1; i <= 6; i++) { new WorkerThread("Worker " + i).start(); } } static class WorkerThread extends Thread { private String name; WorkerThread(String name) { this.name = name; } @Override public void run() { try { System.out.println(name + " is trying to acquire a permit..."); // Acquiring the semaphore semaphore.acquire(); System.out.println(name + " acquired a permit."); // Simulating work by sleeping Thread.sleep(2000); System.out.println(name + " is releasing a permit."); } catch (InterruptedException e) { e.printStackTrace(); } finally { // Releasing the semaphore semaphore.release(); } } } }
Dans cet exemple, nous créons un sémaphore avec trois autorisations, ce qui signifie que seuls trois threads peuvent accéder à la section critique du code à un moment donné. Nous créons ensuite six threads, qui tentent tous d'acquérir un permis. Une fois qu'un thread acquiert un permis, il simule un certain travail en dormant pendant deux secondes avant de libérer le permis.
Lorsque vous exécutez le code ci-dessus, le résultat ressemblera à ceci :
Worker 1 is trying to acquire a permit... Worker 1 acquired a permit. Worker 2 is trying to acquire a permit... Worker 2 acquired a permit. Worker 3 is trying to acquire a permit... Worker 3 acquired a permit. Worker 4 is trying to acquire a permit... Worker 5 is trying to acquire a permit... Worker 6 is trying to acquire a permit... Worker 1 is releasing a permit. Worker 4 acquired a permit. Worker 2 is releasing a permit. Worker 5 acquired a permit. Worker 3 is releasing a permit. Worker 6 acquired a permit.
Ici, les trois premiers threads acquièrent avec succès les permis et commencent leurs tâches. Les threads restants doivent attendre qu'une autorisation soit libérée avant de pouvoir continuer.
Les sémaphores sont particulièrement utiles dans les scénarios où vous devez limiter le nombre d'accès simultanés à une ressource particulière, tels que :
Bien que les sémaphores soient un outil puissant, ils présentent leurs propres avantages et inconvénients.
Flexibilité : Les sémaphores permettent un contrôle précis de l'accès aux ressources par plusieurs threads.
Evolutivité : Les sémaphores peuvent facilement gérer l'accès à un grand nombre de ressources.
Équité : Les sémaphores peuvent être configurés pour garantir que les threads acquièrent les permis de manière équitable.
Complexité : L'utilisation de sémaphores peut introduire de la complexité dans votre code, rendant plus difficile le débogage.
Deadlocks : S'ils ne sont pas gérés correctement, les sémaphores peuvent conduire à des blocages où les threads sont indéfiniment bloqués en attente d'autorisations.
Pour éviter les pièges courants et tirer le meilleur parti des sémaphores, tenez compte des bonnes pratiques suivantes :
Au lieu d'utiliser acquire(), qui bloque indéfiniment, vous pouvez utiliser tryAcquire() pour tenter d'acquérir un permis avec un délai d'attente. Cela évite que les discussions ne restent bloquées en attente.
import java.util.concurrent.Semaphore; public class SemaphoreDemo { // Creating a semaphore with 3 permits private static final Semaphore semaphore = new Semaphore(3); public static void main(String[] args) { // Creating and starting 6 threads for (int i = 1; i <= 6; i++) { new WorkerThread("Worker " + i).start(); } } static class WorkerThread extends Thread { private String name; WorkerThread(String name) { this.name = name; } @Override public void run() { try { System.out.println(name + " is trying to acquire a permit..."); // Acquiring the semaphore semaphore.acquire(); System.out.println(name + " acquired a permit."); // Simulating work by sleeping Thread.sleep(2000); System.out.println(name + " is releasing a permit."); } catch (InterruptedException e) { e.printStackTrace(); } finally { // Releasing the semaphore semaphore.release(); } } } }
Pour éviter les fuites de ressources, libérez toujours le permis dans un bloc finally. Cela garantit que le permis est libéré même en cas d'exception.
Si vous n'avez besoin de verrouiller et de déverrouiller une ressource que pour un seul thread, envisagez d'utiliser ReentrantLock ou synchronisé au lieu d'un sémaphore binaire.
Les sémaphores sont un outil puissant de gestion de la concurrence en Java, vous permettant de contrôler le nombre de threads accédant à une ressource partagée. En suivant les techniques et les bonnes pratiques décrites dans cet article, vous pouvez implémenter efficacement des sémaphores dans vos applications Java pour garantir une gestion sûre et efficace des ressources.
Si vous avez des questions ou souhaitez partager vos propres expériences avec les sémaphores, n'hésitez pas à commenter ci-dessous !
Lisez les articles plus sur : Techniques de gestion de la concurrence en Java à l'aide de sémaphores
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!