Was ist Java Semaphore und Mutex – Java Concurrency MultiThread erklärt mit Beispiel
Veröffentlicht: 2015-03-12Java Concurrency ist ein sehr weites Thema. Es stehen Hunderte von Tutorials und Beispielen zur Verfügung, die verwendet werden können. Vor einiger Zeit habe ich einige Tutorials zum gleichzeitigen Ausführen mehrerer Threads in Java und zu verschiedenen Arten von synchronisierten Blöcken geschrieben.
In diesem Tutorial werden wir Folgendes durchgehen:
- Erklärung von Mutex
- Erklärung von Semaphor
- Zwei Beispiele mit Details
Lass uns anfangen
Let's keep this in mind
folgende Erklärung lesen:
- Nehmen Sie ein Beispiel für Shopper und Customer
- Käufer leiht sich Laptops aus
- Der Kunde kann kommen und den Laptop benutzen – der Kunde benötigt einen Schlüssel, um einen Laptop zu benutzen
- Nach der Verwendung kann der Kunde den Laptop an Shopper zurückgeben
Was ist Mutex (Nur 1 Thread):
Käufer hat einen Schlüssel zu einem Laptop. Ein Kunde kann sich den Schlüssel – Laptop – zur Zeit ausleihen. Wenn die Aufgabe beendet ist, gibt (befreit) der Käufer den Schlüssel an den nächsten Kunden in der Warteschlange.
Official Definition
:
„Mutex wird normalerweise verwendet, um den Zugriff auf einen Abschnitt von re-entrant code
zu serialisieren, cannot be executed concurrently
werden kann. Ein Mutex-Objekt lässt nur einen Thread in einen kontrollierten Abschnitt zu und zwingt andere Threads, die versuchen, Zugriff auf diesen Abschnitt zu erhalten, zu warten, bis der erste Thread diesen Abschnitt verlassen hat.“
Mit anderen Worten: Mutex = Mutually Exclusive Semaphore
Was ist Semaphore (N angegebene Threads):
Nehmen wir an, Shopper hat jetzt 3 identische Laptops und 3 identische Schlüssel. Semaphore ist die Anzahl der free identical Laptop keys
. Der Semaphor-Zähler – die Anzahl der Schlüssel – wird zu Beginn auf 3 gesetzt (alle drei Laptops sind frei), dann wird der Zählwert verringert, wenn der Kunde hereinkommt. Wenn alle Laptops verwendet werden, dh es gibt keine freien Schlüssel mehr für Laptop, die Semaphor-Zählung ist 0. Wenn nun einer der Kunden den Laptop zurückgibt, wird die Semaphore auf 1 erhöht (ein kostenloser Schlüssel) und an den nächsten Kunden in der Warteschlange weitergegeben.
Official Definition
: „Ein Semaphor begrenzt die Anzahl gleichzeitiger Benutzer einer gemeinsam genutzten Ressource auf eine maximale Anzahl. Threads können den Zugriff auf die Ressource anfordern (Verringern des Semaphors) und signalisieren, dass sie die Verwendung der Ressource beendet haben (Erhöhen des Semaphors).“
Ein weiteres Muss ist: Lazy Creation of Singleton ThreadSafe Instance
Beispiel-1: (Erklärung unten)
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 ( ) ; } } |
Im obigen Tutorial CrunchifySemaphoreMutexTutorial.java
kann der CrunchifyProducer, wenn er threadName
zum crunchifyList
-Objekt CrunchifyProducer
hinzufügt, das Semaphor signalisieren.
Der CrunchifyConsumer
kann dann versuchen, das Semaphor zu erwerben, sodass er wartet, bis der CrunchifyProducer signalisiert hat, dass eine Thread-ID hinzugefügt wurde. Beim Signalisieren hinzugefügter Daten wird einer der Verbraucher geweckt und weiß, dass er ein crunchifyList-Objekt lesen kann. Es kann eine Liste lesen und dann wieder versuchen, die Semaphore zu erfassen.
Wenn der Erzeuger in dieser Zeit ein weiteres Paket geschrieben hat, hat er erneut signalisiert und einer der Verbraucher wird dann ein weiteres Paket lesen und so weiter …
Mit anderen Worten:
1 2 3 |
CrunchifyProducer : Add an object o List - Semaphore . release ( 1 ) CrunchifyConsumer x N ) - Semaphore . acquire ( 1 ) - Read an object from List |

Ergebnis:
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 . . . . . . . . . . . . . . . |
So verhindern Sie Race-Condition:
What if you have multiple Consumers?
Im obigen Java-Tutorial sollten die Verbraucher (nicht der Erzeuger) den Puffer sperren, wenn sie das Paket lesen (aber nicht, wenn sie das Semaphor erhalten), um Race-Bedingungen zu verhindern. Im Beispiel unten sperrt der Producer auch die Liste, da sich alles auf derselben JVM befindet.
Beispiel-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 ( ) ; } } |
Ergebnis:
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 ( ) |