Co to jest Java Semaphore i Mutex – Java Concurrency MultiThread wyjaśnione na przykładzie
Opublikowany: 2015-03-12Współbieżność Java to bardzo szeroki temat. Dostępne są setki samouczków i przykładów. Jakiś czas temu napisałem kilka samouczków na temat uruchamiania wielu wątków jednocześnie w Javie i różnych typów zsynchronizowanych bloków.
W tym samouczku omówimy:
- Wyjaśnienie Mutex
- Wyjaśnienie semafora
- Dwa przykłady ze szczegółami
Zacznijmy
Let's keep this in mind
czytając poniższe wyjaśnienie:
- Weź przykład Kupującego i Klienta
- Kupujący pożycza laptopy
- Klient może przyjść i skorzystać z laptopa – klient potrzebuje klucza do korzystania z laptopa
- Po użyciu – klient może zwrócić laptopa do Kupującego
Co to jest Mutex (tylko 1 wątek):
Kupujący ma klucz do laptopa. Jeden klient może mieć klucz – pożyczyć laptopa – w tym czasie. Po zakończeniu zadania Klient oddaje (uwalnia) klucz kolejnemu klientowi w kolejce.
Official Definition
:
„Mutex jest zwykle używany do serializacji dostępu do sekcji re-entrant code
, który cannot be executed concurrently
przez więcej niż jeden wątek. Obiekt mutex pozwala tylko na jeden wątek w kontrolowanej sekcji, zmuszając inne wątki, które próbują uzyskać dostęp do tej sekcji, do czekania, aż pierwszy wątek opuści tę sekcję.”
Innymi słowy: Mutex = Mutually Exclusive Semaphore
Co to jest Semafor (N określonych wątków):
Załóżmy, że teraz Shopper ma 3 identyczne laptopy i 3 identyczne klucze. Semafor to liczba free identical Laptop keys
. Liczba semaforów – liczba kluczy – jest na początku ustawiona na 3 (wszystkie trzy laptopy są wolne), a następnie wartość licznika jest zmniejszana w miarę przychodzenia Klienta. Jeśli wszystkie laptopy są w użyciu, tj. nie ma wolnych kluczy dla Laptop, liczba semaforów wynosi 0. Teraz, gdy któryś z klientów zwraca Laptop, semafor jest zwiększany do 1 (jeden wolny klucz) i przekazywany następnemu klientowi w kolejce.
Official Definition
: „Semafor ogranicza liczbę jednoczesnych użytkowników współdzielonego zasobu do maksymalnej liczby. Wątki mogą żądać dostępu do zasobu (zmniejszanie semafora) i mogą sygnalizować, że zakończyły korzystanie z zasobu (zwiększenie semafora).”
Kolejna musi przeczytać: Lazy Creation of Singleton ThreadSafe Instance
Przykład-1: (wyjaśnienie poniżej)
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 ( ) ; } } |
W powyższym samouczku CrunchifySemaphoreMutexTutorial.java
, gdy CrunchifyProducer
dodaje threadName
do obiektu crunchifyList
crunchifyList, może to sygnalizować semafor.
CrunchifyConsumer
może wtedy próbować uzyskać semafor, więc będzie czekał, aż CrunchifyProducer zasygnalizuje dodanie threadID. Po zasygnalizowaniu dodanych danych jeden z konsumentów zostanie obudzony i będzie wiedział, że może odczytać obiekt crunchifyList. Może przeczytać listę, a następnie wrócić do próby zdobycia semafora.
Jeśli w tym czasie producent napisał kolejny pakiet, zasygnalizował ponownie, a następnie jeden z konsumentów odczyta kolejny pakiet i tak dalej…
Innymi słowy:
1 2 3 |
CrunchifyProducer : Add an object o List - Semaphore . release ( 1 ) CrunchifyConsumer x N ) - Semaphore . acquire ( 1 ) - Read an object from List |

Wynik:
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 . . . . . . . . . . . . . . . |
Jak zapobiec sytuacji wyścigu:
What if you have multiple Consumers?
W powyższym samouczku Java Konsumenci (nie producenci) powinni blokować bufor podczas odczytu pakietu (ale nie podczas uzyskiwania semafora), aby zapobiec sytuacji wyścigu. W poniższym przykładzie producent również blokuje listę, ponieważ wszystko jest na tej samej JVM.
Przykład-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 ( ) ; } } |
Wynik:
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 ( ) |