Che cos'è Java Semaphore e Mutex – Java Concurrency MultiThread spiegato con l'esempio
Pubblicato: 2015-03-12La concorrenza Java è un argomento molto ampio. Ci sono centinaia di tutorial ed esempi disponibili per l'uso. Qualche tempo fa ho scritto alcuni tutorial su Esegui più thread contemporaneamente in Java e diversi tipi di blocchi sincronizzati.
In questo tutorial andremo oltre:
- Spiegazione di Mutex
- Spiegazione del semaforo
- Due esempi con dettagli
Iniziamo
Let's keep this in mind
mentre leggiamo la spiegazione di seguito:
- Prendi un esempio di Acquirente e Cliente
- L'acquirente prende in prestito dei laptop
- Il cliente può venire e utilizzare il laptop: il cliente ha bisogno di una chiave per utilizzare un laptop
- Dopo l'uso: il cliente può restituire il laptop all'acquirente
Che cos'è Mutex (solo 1 thread):
L'acquirente ha una chiave per un laptop. Un cliente può avere la chiave - prendere in prestito un laptop - alla volta. Al termine dell'attività, l'Acquirente consegna (libera) la chiave al prossimo cliente in coda.
Official Definition
:
“Mutex viene in genere utilizzato per serializzare l'accesso a una sezione di re-entrant code
che cannot be executed concurrently
da più di un thread. Un oggetto mutex consente a un solo thread di entrare in una sezione controllata, costringendo gli altri thread che tentano di accedere a quella sezione ad attendere che il primo thread sia uscito da quella sezione.
In altre parole: Mutex = Mutually Exclusive Semaphore
Che cos'è il semaforo (N thread specificati):
Diciamo che ora Shopper ha 3 laptop identici e 3 tasti identici. Il semaforo è il numero di free identical Laptop keys
. Il conteggio del semaforo - il conteggio delle chiavi - è impostato su 3 all'inizio (tutti e tre i laptop sono liberi), quindi il valore del conteggio viene decrementato quando i clienti stanno entrando. Se tutti i laptop sono in uso, ovvero non ci sono chiavi libere per Laptop, il conteggio del semaforo è 0. Ora, quando uno dei clienti restituisce il laptop, il semaforo viene aumentato a 1 (una chiave gratuita) e assegnato al cliente successivo nella coda.
Official Definition
: “Un semaforo limita il numero di utenti simultanei di una risorsa condivisa fino a un numero massimo. I thread possono richiedere l'accesso alla risorsa (diminuendo il semaforo) e possono segnalare che hanno terminato di utilizzare la risorsa (incrementando il semaforo)."
Un altro deve leggere: Creazione pigra di istanza ThreadSafe Singleton
Esempio-1: (spiegazione di seguito)
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 ( ) ; } } |
Nel tutorial sopra CrunchifySemaphoreMutexTutorial.java
quando CrunchifyProducer
aggiunge threadName
all'oggetto crunchifyList
linkedList può segnalare il semaforo.
Il CrunchifyConsumer
può quindi provare ad acquisire il semaforo in modo che aspetteranno fino a quando CrunchifyProducer non avrà segnalato che è stato aggiunto un threadID. Dopo aver segnalato un dato aggiunto, uno dei consumatori verrà svegliato e saprà di poter leggere un oggetto crunchifyList. Può leggere un elenco, quindi tornare a provare ad acquisire sul semaforo.
Se in quel tempo il produttore ha scritto un altro pacchetto, ha segnalato di nuovo e uno dei consumatori andrà quindi a leggere un altro pacchetto e così via...
In altre parole:
1 2 3 |
CrunchifyProducer : Add an object o List - Semaphore . release ( 1 ) CrunchifyConsumer x N ) - Semaphore . acquire ( 1 ) - Read an object from List |

Risultato:
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 . . . . . . . . . . . . . . . |
Come prevenire la condizione di razza:
What if you have multiple Consumers?
Tutorial Java sopra I consumatori (non il produttore) dovrebbero bloccare il buffer durante la lettura del pacchetto (ma non durante l'acquisizione del semaforo) per evitare condizioni di gara. Nell'esempio seguente il produttore blocca anche l'elenco poiché tutto è sulla stessa JVM.
Esempio-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 ( ) ; } } |
Risultato:
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 ( ) |