Ce este Java Semaphore și Mutex – Java Concurrency MultiThread explicat cu Exemplu
Publicat: 2015-03-12Java Concurrency este un subiect foarte larg. Există sute de tutoriale și exemple disponibile pentru utilizare. Cu ceva timp în urmă, am scris câteva tutoriale despre Run Multiple Threads simultan în Java și diferite tipuri de blocuri sincronizate.
În acest tutorial vom trece peste:
- Explicația Mutex
- Explicația Semaforului
- Două exemple cu detalii
Să începem
Let's keep this in mind
timp ce citim explicația de mai jos:
- Luați un exemplu de cumpărător și client
- Cumpărătorul împrumută laptopuri
- Clientul poate veni și utiliza laptopul – clientul are nevoie de o cheie pentru a utiliza un laptop
- După utilizare – clientul poate returna laptopul la Cumpărător
Ce este Mutex (doar 1 fir):
Cumpărătorul are cheia unui laptop. Un client poate avea cheia – să împrumute un laptop – la momentul respectiv. Când sarcina se termină, Cumpărătorul dă (eliberează) cheia următorului client din coadă.
Official Definition
:
„Mutex este folosit în mod obișnuit pentru a serializa accesul la o secțiune a re-entrant code
care cannot be executed concurrently
de mai mult de un fir. Un obiect mutex permite doar un fir într-o secțiune controlată, forțând alte fire care încearcă să obțină acces la acea secțiune să aștepte până când primul fir a ieșit din acea secțiune.”
Cu alte cuvinte: Mutex = Mutually Exclusive Semaphore
Ce este Semaphore (N fire specificate):
Să presupunem că acum Shopper are 3 laptopuri identice și 3 chei identice. Semaforul este numărul de free identical Laptop keys
. Numărul de semafor – numărul de chei – este setat la 3 la început (toate cele trei laptopuri sunt gratuite), apoi valoarea numărului este diminuată pe măsură ce clientul intră. Dacă toate laptopurile sunt în uz, adică nu mai sunt chei libere pentru Laptop, numărul de semafor este 0. Acum, când oricare dintre clienți returnează laptopul, semaforul este mărit la 1 (o cheie gratuită) și dat următorului client din coadă.
Official Definition
: „Un semafor restricționează numărul de utilizatori simultani ai unei resurse partajate până la un număr maxim. Threadurile pot solicita acces la resursă (decrementând semaforul) și pot semnala că au terminat de utilizat resursa (incrementând semaforul).”
Un alt trebuie citit: Lazy Creation of Singleton ThreadSafe Instance
Exemplul-1: (explicația de mai jos)
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 ( ) ; } } |
În tutorialul de mai sus CrunchifySemaphoreMutexTutorial.java
, când CrunchifyProducer
adaugă threadName
la obiectul crunchifyList
linkedList, poate semnala semaforul.
CrunchifyConsumer
poate încerca apoi să achiziționeze semaforul, așa că va aștepta până când CrunchifyProducer a semnalat că a fost adăugat un threadID. La semnalarea unei date adăugate, unul dintre consumatori va fi trezit și va ști că poate citi un obiect crunchifyList. Poate citi o listă, apoi se întoarce la încercarea de a obține pe semafor.
Dacă în acel timp producătorul a scris un alt pachet, a semnalat din nou și oricare dintre consumatori va continua să citească un alt pachet și așa mai departe...
Cu alte cuvinte:
1 2 3 |
CrunchifyProducer : Add an object o List - Semaphore . release ( 1 ) CrunchifyConsumer x N ) - Semaphore . acquire ( 1 ) - Read an object from List |

Rezultat:
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 . . . . . . . . . . . . . . . |
Cum să preveniți starea de cursă:
What if you have multiple Consumers?
În tutorialul Java de mai sus Consumatorii (nu producătorul) ar trebui să blocheze tamponul atunci când citesc pachetul (dar nu când achiziționează semaforul) pentru a preveni condițiile de cursă. În exemplul de mai jos, producătorul blochează și lista, deoarece totul este pe același JVM.
Exemplul-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 ( ) ; } } |
Rezultat:
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 ( ) |