Qu'est-ce que Java Semaphore et Mutex - Java Concurrency MultiThread expliqué avec l'exemple
Publié: 2015-03-12La concurrence Java est un sujet très vaste. Il existe des centaines de tutoriels et d'exemples disponibles à utiliser. Il y a quelque temps, j'ai écrit quelques tutoriels sur Exécuter plusieurs threads simultanément en Java et sur différents types de blocs synchronisés.
Dans ce tutoriel, nous allons passer en revue :
- Explication de Mutex
- Explication du sémaphore
- Deux exemples avec détails
Commençons
Let's keep this in mind
en lisant l'explication ci-dessous :
- Prenons un exemple d'acheteur et de client
- L'acheteur emprunte des ordinateurs portables
- Le client peut venir utiliser un ordinateur portable - le client a besoin d'une clé pour utiliser un ordinateur portable
- Après utilisation - le client peut retourner l'ordinateur portable à l'acheteur
Qu'est-ce que Mutex (juste 1 thread):
L'acheteur a la clé d'un ordinateur portable. Un client peut avoir la clé – emprunter un ordinateur portable – à la fois. Lorsque la tâche est terminée, le client donne (libère) la clé au client suivant dans la file d'attente.
Official Definition
:
"Mutex est généralement utilisé pour sérialiser l'accès à une section de re-entrant code
qui cannot be executed concurrently
par plus d'un thread. Un objet mutex n'autorise qu'un seul thread dans une section contrôlée, forçant les autres threads qui tentent d'accéder à cette section à attendre que le premier thread soit sorti de cette section.
En d'autres termes : Mutex = Mutually Exclusive Semaphore
Qu'est-ce que Semaphore (N threads spécifiés):
Disons maintenant que Shopper a 3 ordinateurs portables identiques et 3 clés identiques. Le sémaphore est le nombre de free identical Laptop keys
. Le nombre de sémaphores - le nombre de clés - est défini sur 3 au début (les trois ordinateurs portables sont libres), puis la valeur de comptage est décrémentée au fur et à mesure que le client entre. Si tous les ordinateurs portables sont utilisés, c'est-à-dire qu'il ne reste plus de clés libres pour Ordinateur portable, le nombre de sémaphores est 0. Désormais, lorsque l'un des clients renvoie l'ordinateur portable, le sémaphore est augmenté à 1 (une clé libre) et donné au client suivant dans la file d'attente.
Official Definition
: « Un sémaphore restreint le nombre d'utilisateurs simultanés d'une ressource partagée jusqu'à un nombre maximum. Les threads peuvent demander l'accès à la ressource (en décrémentant le sémaphore), et peuvent signaler qu'ils ont fini d'utiliser la ressource (en incrémentant le sémaphore).
Un autre doit lire: Lazy Creation of Singleton ThreadSafe Instance
Exemple-1 : (explication ci-dessous)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package crunchify . com . tutorial ; import java . util . LinkedList ; import java . util . concurrent . Semaphore ; /** * @author Crunchify.com * */ public class CrunchifySemaphoreMutexTutorial { static Object crunchifyLock = new Object ( ) ; static LinkedList <String> crunchifyList = new LinkedList <String> ( ) ; // Semaphore maintains a set of permits. // Each acquire blocks if necessary until a permit is available, and then takes it. // Each release adds a permit, potentially releasing a blocking acquirer. static Semaphore semaphore = new Semaphore ( 0 ) ; static Semaphore mutex = new Semaphore ( 1 ) ; // I'll producing new Integer every time static class CrunchifyProducer extends Thread { public void run ( ) { int counter = 1 ; try { while ( true ) { String threadName = Thread . currentThread ( ) . getName ( ) + counter ++ ; mutex . acquire ( ) ; crunchifyList . add ( threadName ) ; System . out . println ( "Producer is prdoucing new value: " + threadName ) ; mutex . release ( ) ; // release lock semaphore . release ( ) ; Thread . sleep ( 500 ) ; } } catch ( Exception x ) { x . printStackTrace ( ) ; } } } // I'll be consuming Integer every stime static class CrunchifyConsumer extends Thread { String consumerName ; public CrunchifyConsumer ( String name ) { this . consumerName = name ; } public void run ( ) { try { while ( true ) { // acquire lock. Acquires the given number of permits from this semaphore, blocking until all are // available // process stops here until producer releases the lock semaphore . acquire ( ) ; // Acquires a permit from this semaphore, blocking until one is available mutex . acquire ( ) ; String result = "" ; for ( String value : crunchifyList ) { result = value + "," ; } System . out . println ( consumerName + " consumes value: " + result + "crunchifyList.size(): " + crunchifyList . size ( ) + "\n" ) ; mutex . release ( ) ; } } catch ( Exception e ) { e . printStackTrace ( ) ; } } } public static void main ( String [ ] args ) { new CrunchifyProducer ( ) . start ( ) ; new CrunchifyConsumer ( "Crunchify" ) . start ( ) ; new CrunchifyConsumer ( "Google" ) . start ( ) ; new CrunchifyConsumer ( "Yahoo" ) . start ( ) ; } } |
Dans le didacticiel ci-dessus CrunchifySemaphoreMutexTutorial.java
lorsque CrunchifyProducer
ajoute threadName
à l'objet crunchifyList
linkedList, il peut signaler le sémaphore.
Le CrunchifyConsumer
peut alors essayer d'acquérir le sémaphore, il attendra donc que le CrunchifyProducer ait signalé qu'un threadID a été ajouté. Lors de la signalisation d'une donnée ajoutée, l'un des consommateurs sera réveillé et il saura qu'il peut lire un objet crunchifyList. Il peut lire une liste, puis recommencer à essayer d'acquérir sur le sémaphore.
Si, pendant ce temps, le producteur a écrit un autre paquet, il a signalé à nouveau et l'un ou l'autre des consommateurs continuera alors à lire un autre paquet et ainsi de suite…
Autrement dit:
1 2 3 |
CrunchifyProducer : Add an object o List - Semaphore . release ( 1 ) CrunchifyConsumer x N ) - Semaphore . acquire ( 1 ) - Read an object from List |

Résultat:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Producer is prdoucing new value : Thread - 01 Crunchify consumes value : Thread - 01 , crunchifyList . size ( ) : 1 Producer is prdoucing new value : Thread - 02 Google consumes value : Thread - 02 , crunchifyList . size ( ) : 2 Producer is prdoucing new value : Thread - 03 Yahoo consumes value : Thread - 03 , crunchifyList . size ( ) : 3 Producer is prdoucing new value : Thread - 04 Crunchify consumes value : Thread - 04 , crunchifyList . size ( ) : 4 . . . . . . . . . . . . . . . |
Comment prévenir les conditions de concurrence :
What if you have multiple Consumers?
Dans le didacticiel Java ci-dessus, les consommateurs (et non le producteur) doivent verrouiller le tampon lors de la lecture du paquet (mais pas lors de l'acquisition du sémaphore) pour éviter les conditions de concurrence. Dans l'exemple ci-dessous, le producteur verrouille également la liste puisque tout est sur la même JVM.
Exemple-2 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package crunchify . com . tutorial ; import java . util . concurrent . Semaphore ; /** * @author Crunchify.com * */ public class CrunchifyJavaSemaphoreTutorial { private static final int MAX_CONCURRENT_THREADS = 2 ; private final Semaphore crunchifyAdminLOCK = new Semaphore ( MAX_CONCURRENT_THREADS , true ) ; public void crunchifyStartTest ( ) { for ( int i = 0 ; i < 10 ; i ++ ) { CrunchifyPerson person = new CrunchifyPerson ( ) ; person . start ( ) ; } } public class CrunchifyPerson extends Thread { @Override public void run ( ) { try { // Acquire Lock crunchifyAdminLOCK . acquire ( ) ; } catch ( InterruptedException e ) { System . out . println ( "received InterruptedException" ) ; return ; } System . out . println ( "Thread " + this . getId ( ) + " start using Crunchify's car - Acquire()" ) ; try { sleep ( 1000 ) ; } catch ( Exception e ) { } finally { // Release Lock crunchifyAdminLOCK . release ( ) ; } System . out . println ( "Thread " + this . getId ( ) + " stops using Crunchify's car - Release()\n" ) ; } } public static void main ( String [ ] args ) { CrunchifyJavaSemaphoreTutorial test = new CrunchifyJavaSemaphoreTutorial ( ) ; test . crunchifyStartTest ( ) ; } } |
Résultat:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Thread 11 start using Crunchify 's car - Acquire() Thread 10 start using Crunchify' s car - Acquire ( ) Thread 10 stops using Crunchify 's car - Release() Thread 12 start using Crunchify' s car - Acquire ( ) Thread 13 start using Crunchify 's car - Acquire() Thread 11 stops using Crunchify' s car - Release ( ) Thread 13 stops using Crunchify 's car - Release() Thread 15 start using Crunchify' s car - Acquire ( ) Thread 14 start using Crunchify 's car - Acquire() Thread 12 stops using Crunchify' s car - Release ( ) Thread 14 stops using Crunchify 's car - Release() Thread 16 start using Crunchify' s car - Acquire ( ) Thread 15 stops using Crunchify 's car - Release() Thread 17 start using Crunchify' s car - Acquire ( ) Thread 17 stops using Crunchify 's car - Release() Thread 18 start using Crunchify' s car - Acquire ( ) Thread 19 start using Crunchify 's car - Acquire() Thread 16 stops using Crunchify' s car - Release ( ) Thread 18 stops using Crunchify 's car - Release() Thread 19 stops using Crunchify' s car - Release ( ) |