Commit 6ebb4ddc authored by Blanke, Daniela's avatar Blanke, Daniela
Browse files

Update concurrency-sync.html

parent 40d39745
Pipeline #8035 passed with stage
in 1 minute and 11 seconds
<p>Der Zugriff auf gemeinsame Ressourcen, z.B. auf Objekte in Java, aus mehreren nebenläufigen Threads heraus, kann ohne Synchronisation zu inkonsistenten, ungewollten Zuständen führen. Dies tritt insbesondere dann auf wenn mehrere Anweisungen im Kontrollfluss als eine atomare Aktion ausgeführt werden sollen, deren Effekt unbeeinflusst von Anweisungen sein muss, die in nebenläufigen Threads ausgeführt werden. Ähnliche Probleme treten im Umfeld von Datenbanken auf, wenn Transaktionen nicht ausreichend isoliert voneinander sind (s. <a href="https://de.wikipedia.org/wiki/ACID">ACID-Prinzip</a>).</p>
<p>Der Zugriff auf gemeinsame Ressourcen, z.B. auf Objekte in Java, aus mehreren nebenläufigen Threads heraus, kann ohne Synchronisation zu inkonsistenten, ungewollten Zuständen führen. Dies tritt insbesondere dann auf, wenn mehrere Anweisungen im Kontrollfluss als eine atomare Aktion ausgeführt werden sollen, deren Effekt unbeeinflusst von Anweisungen sein muss, die in nebenläufigen Threads ausgeführt werden. Ähnliche Probleme treten im Umfeld von Datenbanken auf, wenn Transaktionen nicht ausreichend isoliert voneinander sind (s. <a href="https://de.wikipedia.org/wiki/ACID">ACID-Prinzip</a>).</p>
<h4>Race Conditions</h4>
......@@ -69,7 +69,7 @@ class LostUpdate {
}
}</code></pre>
<p>Es existiert ein sogenannter kritischer Abschnitt (engl. <i>Critical Section</i>), indem sich nur ein Thread zurzeit befinden darf, damit keine Lost Updates auftreten. In dem obigen Code-Beispiel ist die Methode <code>incrementResourceValue</code> der kritische Abschnitt (Zeilen 8-12). Das Lesen, Inkrementieren und Schreiben des Zustands der Ressource muss als eine atomare Aktion ausgeführt werden und darf nicht durch eine Verschränkung der Threads unterbrochen werden. Eine naheliegende Idee ist es, den kritischen Abschnitt auf eine einzelne Anweisung zu reduzieren, z.B. indem die Methode <code>incrementResourceValue</code> wie folgt umgestaltet wird:
<p>Es existiert ein sogenannter kritischer Abschnitt (engl. <i>Critical Section</i>), indem sich nur ein Thread zurzeit befinden darf, damit keine Lost Updates auftreten. In dem obigen Code-Beispiel ist die Methode <code>incrementResourceValue</code> der kritische Abschnitt (Zeilen 8-12). Das Lesen, Inkrementieren und Schreiben des Zustands der Ressource muss als eine atomare Aktion ausgeführt werden und darf nicht durch eine Verschränkung der Threads unterbrochen werden. Eine naheliegende Idee ist es, den kritischen Abschnitt auf eine einzelne Anweisung zu reduzieren, z.B. indem die Methode <code>incrementResourceValue</code> wie folgt umgestaltet wird:</p>
<pre><code class="language-java line-numbers">void incrementResourceValue() {
resource++; // read, increment, and write resource value
......@@ -96,13 +96,13 @@ class LostUpdate {
<li>Ein Thread gibt die erlangte Sperre wieder frei und informiert die blockierten Threads darüber.</li>
</ol>
<p>In Java muss das Schlüsselwort <code>synchronized</code> verwendet werden, um eine Methode oder einen beliebigen Anweisungsblock als kritischen Abschnitt zu kennzeichnen. Es gewährleistet, dass nur ein Thread gleichzeitig den kritischen Abschnitt betreten und durchlaufen darf. Erst durch die Verwendung des Schlüsselworts <code>synchronized</code> ist die folgende Methode tatsächlich <i>thread-safe</i>.
<p>In Java muss das Schlüsselwort <code>synchronized</code> verwendet werden, um eine Methode oder einen beliebigen Anweisungsblock als kritischen Abschnitt zu kennzeichnen. Es gewährleistet, dass nur ein Thread gleichzeitig den kritischen Abschnitt betreten und durchlaufen darf. Erst durch die Verwendung des Schlüsselworts <code>synchronized</code> ist die folgende Methode tatsächlich <i>thread-safe</i></p>.
<pre><code class="language-java line-numbers">synchronized void incrementResourceValue() {
resource++;
}</code></pre>
<p>Während sich ein Thread im kritischen Abschnitt befindet, sichert die JVM durch eine Sperre zu, dass weitere Threads am Anfang des kritischen Abschnitts blockiert werden. Dadurch wechseln die Threads ggf. aus dem Zustand <b>Runnable</b> in den Zustand <b>Blocked</b>. Eine Sperre wird in Java je Objekt gesetzt, d.h. mehrere <code>synchronized</code>-Methoden und -Blöcke, die sich auf dasselbe Objekt beziehen, sind nicht unabhängig voneinander, sondern teilen sich eine gemeinsame Sperre. Das folgende Code-Beispiel zeigt beispielhaft eine Klasse mit zwei <code>synchronized</code>-Methoden und einem <code>synchronized</code>-Block. Wenn ein Objekt X dieser Klasse instantiiert wird und und ein Thread auf diesem Objekt X die Methode <code>a</code> aufruft, so kann kein anderer Thread die Methoden <code>a</code>, <code>b</code> und den <code>synchronized</code>-Block in Methode <code>c</code> betreten. Auf einem weiteren Objekt Y dieser Klasse hingegen, wären dadurch keine Sperren gesetzt und die <code>synchronized</code>-Methoden und -Blöcke könnten weiterhin aufgerufen werden, ohne zu blockieren.</p>
<p>Während sich ein Thread im kritischen Abschnitt befindet, sichert die JVM durch eine Sperre zu, dass weitere Threads am Anfang des kritischen Abschnitts blockiert werden. Dadurch wechseln die Threads ggf. aus dem Zustand <b>Runnable</b> in den Zustand <b>Blocked</b>. Eine Sperre wird in Java je Objekt gesetzt, d.h. mehrere <code>synchronized</code>-Methoden und -Blöcke, die sich auf dasselbe Objekt beziehen, sind nicht unabhängig voneinander, sondern teilen sich eine gemeinsame Sperre. Das folgende Code-Beispiel zeigt beispielhaft eine Klasse mit zwei <code>synchronized</code>-Methoden und einem <code>synchronized</code>-Block. Wenn ein Objekt X dieser Klasse instantiiert wird und ein Thread auf diesem Objekt X die Methode <code>a</code> aufruft, so kann kein anderer Thread die Methoden <code>a</code>, <code>b</code> und den <code>synchronized</code>-Block in Methode <code>c</code> betreten. Auf einem weiteren Objekt Y dieser Klasse hingegen wären dadurch keine Sperren gesetzt und die <code>synchronized</code>-Methoden und -Blöcke könnten weiterhin aufgerufen werden, ohne zu blockieren.</p>
<pre><code class="language-java line-numbers">class SharedResource {
......@@ -128,14 +128,14 @@ class LostUpdate {
<h4>Thread-Zustände in der JVM</h4>
<p>Das folgende UML-Zustandsdiagramm visualisiert die möglichen Zustände eines Threads innerhalb der JVM sowie die möglichen Transitionen zwischen diesen Zuständen. Sperren mittels <code>synchronized</code> gewährleisten den wechselseitigen Ausschluss für kritische Abschnitte und können damit Race Conditions verhindern. Die JVM versetzt die Threads ggf. automatisch in den Zustand <b>Blocked</b>, falls auf eine Sperre bedingt durch <code>synchronized</code> zu warten ist: <i>"Thread is blocked for synchronization"</i>.</p>
<p>Das folgende UML-Zustandsdiagramm visualisiert die möglichen Zustände eines Threads innerhalb der JVM sowie die möglichen Transitionen zwischen diesen Zuständen. Sperren mittels <code>synchronized</code> gewährleisten den wechselseitigen Ausschluss für kritische Abschnitte und können damit Race Conditions verhindern. Die JVM versetzt die Threads ggf. automatisch in den Zustand <b>Blocked</b>, falls auf eine Sperre, bedingt durch <code>synchronized</code>, zu warten ist: <i>"Thread is blocked for synchronization"</i>.</p>
<img src="media/concurrency_thread_states.png" style="width:750px">
<label>Zustände und Transitionen von Threads in der JVM</label>
<span class="source"><a href="https://www.uml-diagrams.org/java-thread-uml-state-machine-diagram-example.html">Java Thread States and Life Cycle</a></span>
<p>Mittels <code>synchronized</code> können aber keine anwendungsspezifischen Bedingungen formuliert werden. Häufig ergibt sich in der Praxis die Situation, dass ein Thread auf ein bestimmtes Ereignis warten muss, bevor er sinnvoll weiterarbeiten kann. Er soll von einem anderen Thread benachrichtigt werden können, wenn dieser das Ereignis herbeigeführt hat, das aus dem Wartezustand befreit. Der grundlegende Mechanismus für das Warten auf Benachrichtigungen von anderen Threads kann in Java über die Methoden <code>wait</code> und <code>notify</code> abgebildet werden, die in der allgemeinen Oberklasse <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html"><code>java.lang.Object</code></a> implementiert sind und damit jedem Objekt zur Verfügung stehen. Wenn ein Thread die Methode <code>wait</code> auf einem Objekt aufruft, wird er in den Zustand <b>Waiting</b> versetzt: <i>"Thread is waiting for notification"</i>. Erst wenn ein anderer Thread die Methode <code>notify</code> oder die Methode <code>notifyAll</code> auf demselben Objekt aufruft, wird der wartende Thread wieder aus dem Wartezustand befreit und in den Zustand <b>Runnable</b> versetzt. Die Methode <code>wait</code> kann auch mit einem Timeout-Argument versehen werden. In diesem Fall wird der Thread, wie bei einem Aufruf der Methode <code>Thread.sleep</code>, in den Zustand <b>Timed Waiting</b> versetzt. Über <code>java.lang.management.ThreadInfo</code> kann grundsätzlich ausgewertet werden, wie viel Zeit ein Thread in welchem der Zustände verbracht hat.</p>
</p>
<p>Der Zustand <b>Runnable</b> ist in die Zustände <b>Ready</b> und <b>Running</b> untergliedert. Wenn es mehr nebenläufige Threads als Prozessoren gibt, steuert der Scheduler den Wechsel der Threads zwischen diesen Zuständen. Im Zustand <b>Running</b> ist einem Thread tatsächlich ein Prozessor zugewiesen, so dass der Kontrollfluss des Threads abgearbeitet werden kann. Ein Thread im Zustand <b>Ready</b> wartet darauf, dass der Scheduler ihm im Zuge des Interleaving einen Prozessor zuweist.</p>
......@@ -261,7 +261,7 @@ class BoundedBufferSync implements BoundedBuffer {
</div>
</div>
<span>Es ist anzumerken, dass die Methode <code>notify</code> nur einen zufälligen Thread aus der Menge der aktuell auf die betroffene Sperre wartenden Threads aus dem Wartezustand befreit. Dadurch ist keine Fairness unter den wartenden Erzeugern und Verbrauchern gewährleistet. Das folgende Beispiel widmet sich der Herausforderung faires Warten gemäß des Prinzips einer FIFO-Warteschlange zu realisieren. Wir stellen uns dazu eine Parkgarage mit einer beschränkten Kapazität an Stellplätzen vor. In diese Parkgarage fahren Autos ein und aus. Wenn alle Stellplätze belegt sind, müssen neu ankommende Autos an einer Schranke vor der Parkgarage in einer FIFO-Warteschlange warten. Die Parkgarage ist die gemeinsam genutzte Ressource für mehrere nebenläufige Threads. Die Autos werden als Threads implementiert, die in einer Endlosschleife folgende Aktivitäten ausführen (Zeilen 22-27 in Klasse <code>Car</code>):</span>
<span>Es ist anzumerken, dass die Methode <code>notify</code> nur einen zufälligen Thread aus der Menge, der aktuell auf die betroffene Sperre wartenden Threads, aus dem Wartezustand befreit. Dadurch ist keine Fairness unter den wartenden Erzeugern und Verbrauchern gewährleistet. Das folgende Beispiel widmet sich der Herausforderung faires Warten, gemäß des Prinzips einer FIFO-Warteschlange, zu realisieren. Wir stellen uns dazu eine Parkgarage mit einer beschränkten Kapazität an Stellplätzen vor. In diese Parkgarage fahren Autos ein und aus. Wenn alle Stellplätze belegt sind, müssen neu ankommende Autos an einer Schranke vor der Parkgarage in einer FIFO-Warteschlange warten. Die Parkgarage ist die gemeinsam genutzte Ressource für mehrere nebenläufige Threads. Die Autos werden als Threads implementiert, die in einer Endlosschleife folgende Aktivitäten ausführen (Zeilen 22-27 in Klasse <code>Car</code>):</span>
<ul>
<li>herumfahren (Methode <code>cruise</code> in Klasse <code>Car</code>),</li>
<li>einfahren in die Parkgarage (Methode <code>enter</code> in Interface <code>ParkingGarage</code>),</li>
......@@ -335,7 +335,7 @@ class BoundedBufferSync implements BoundedBuffer {
<span>Die Implementierung <code>FairParkingGarage</code> unterscheidet sich leicht, um die Fairness beim Warten abzubilden – insbesondere in den Zeilen 16 und 32.</span>
<ul>
<li>In Zeile 32 werden mittels der Methode <code>notifyAll</code> alle der wartenden Autos statt nur ein zufälliges benachrichtigt.</li>
<li>In Zeile 16 wird die Bedingung für das Warten erweitert. Es muss nicht nur gewartet werden, wenn keine Stellplätze frei sind (<code>places == 0</code>), sondern auch wenn das Auto noch nicht an vorderster Position der Warteschlange angelangt ist. Es wird dazu gezählt, wie viele Autos bereits an der Schranke vor der Parkgarage angekommen sind (<code>arrivalCount</code>, Zeile 12) und wie viele Autos bereits in die Parkgarage eingefahren sind (<code>entranceCount</code>, Zeile 22). Die Wertzuweisung in die Variable <code>arrival</code> (Zeile 12) entspricht daher der Vergabe einer laufend inkrementierten Wartenummer – ähnlich des Wartebereichs in vielen Ämtern in Deutschland. Ein vor der Schranke wartendes Auto wird zwar jedes Mal erweckt, wenn ein anderes Auto die Parkgarage verlässt, kann aber nur vorrücken und nicht einfahren (<code>arrival - 1 != entranceCount</code>, Zeile 16), bis alle übrigen wartenden Autos mit niedrigerer Wartenummer eingefahren sind. Auf diese Weise wird die Fairness beim Zugriff auf die gemeinsame Ressource eingehalten.</li>
<li>In Zeile 16 wird die Bedingung für das Warten erweitert. Es muss nicht nur gewartet werden, wenn keine Stellplätze frei sind (<code>places == 0</code>), sondern auch, wenn das Auto noch nicht an vorderster Position der Warteschlange angelangt ist. Es wird dazu gezählt, wie viele Autos bereits an der Schranke vor der Parkgarage angekommen sind (<code>arrivalCount</code>, Zeile 12) und wie viele Autos bereits in die Parkgarage eingefahren sind (<code>entranceCount</code>, Zeile 22). Die Wertzuweisung in die Variable <code>arrival</code> (Zeile 12) entspricht daher der Vergabe einer laufend inkrementierten Wartenummer – ähnlich des Wartebereichs in vielen Ämtern in Deutschland. Ein vor der Schranke wartendes Auto wird zwar jedes Mal erweckt, wenn ein anderes Auto die Parkgarage verlässt, kann aber nur vorrücken und nicht einfahren (<code>arrival - 1 != entranceCount</code>, Zeile 16), bis alle übrigen wartenden Autos mit niedrigerer Wartenummer eingefahren sind. Auf diese Weise wird die Fairness beim Zugriff auf die gemeinsame Ressource eingehalten.</li>
</ul>
<ul class="nav nav-tabs" id="sync-fairness2-tabs" role="tablist">
......@@ -422,7 +422,7 @@ class BoundedBufferSync implements BoundedBuffer {
</div>
</div>
<p>Es stellt sich beim genauen Hinsehen die Frage, warum auch am Ende der Methode <code>enter</code> die Methode <code>notifyAll</code> aufgerufen wird (Zeile 25). Dazu müssen wir uns vorstellen, dass die Parkgarage voll belegt ist, während mehrere Autos vor der Schranke warten. Nun verlassen direkt hintereinander mehrere Autos die Parkgarage, so dass wieder einige Stellplätze frei werden. Der gemeinsame kritische Abschnitt der Methoden <code>enter</code> und <code>leave</code> sichert zu, dass während des Ausfahrens eines Autos nicht gleichzeitig ein anderes Auto einfahren kann. Anschließend gelingt es dem vordersten Auto in der Warteschlange in die Parkgarage einzufahren. Nach dem Einfahren muss es die anderen wartenden Autos mittels <code>notifyAll</code> benachrichtigen (Zeile 25), da noch weitere Plätze frei sind und ansonsten nicht wieder belegt werden würden.</p>
<p>Es stellt sich beim genauen Hinsehen die Frage, warum auch am Ende der Methode <code>enter</code> die Methode <code>notifyAll</code> aufgerufen wird (Zeile 25). Dazu müssen wir uns vorstellen, dass die Parkgarage voll belegt ist, während mehrere Autos vor der Schranke warten. Nun verlassen direkt hintereinander mehrere Autos die Parkgarage, sodass wieder einige Stellplätze frei werden. Der gemeinsame kritische Abschnitt der Methoden <code>enter</code> und <code>leave</code> sichert zu, dass während des Ausfahrens eines Autos nicht gleichzeitig ein anderes Auto einfahren kann. Anschließend gelingt es dem vordersten Auto in der Warteschlange in die Parkgarage einzufahren. Nach dem Einfahren muss es die anderen wartenden Autos mittels <code>notifyAll</code> benachrichtigen (Zeile 25), da noch weitere Plätze frei sind und ansonsten nicht wieder belegt werden würden.</p>
<p>Im Package <code>java.util.concurrent</code> finden wir heute lange etablierte Interfaces und Klassen, die eine entwicklerfreundlichere API für die grundlegenden Muster zur Synchronisation von Threads bieten. Dazu zählen z.B. die Interfaces <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/locks/Lock.html"><code>Lock</code></a> als Alternative zu <code>synchronized</code> und <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/locks/Condition.html"><code>Condition</code></a> als Alternative zu den Methoden <code>wait</code>, <code>notify</code> und <code>notifyAll</code>. Blockierende FIFO-Warteschlangen bieten sämtliche Implementierungen des Interface <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/BlockingQueue.html"><code>BlockingQueue</code></a>. Das folgende Code-Beispiel demonstriert wie der oben dargestellte, begrenzte und blockierende Speicher in der Klasse <code>BoundedBufferSync</code> alternativ über <code>Lock</code>- und <code>Condition</code>-Objekte (siehe <code>BoundedBufferLock</code>) bzw. über eine <code>ArrayBlockingQueue</code> (siehe <code>BoundedBufferBlockingQueue</code>) realisiert werden kann.</p>
......@@ -547,7 +547,7 @@ class BoundedBufferBlockingQueue implements BoundedBuffer {
<h4>Deadlock und Starvation</h4>
<p>Wenn ein Thread nicht nur eine sondern mehrere Sperren auf exklusiv nutzbare Ressourcen anfordert, kann es zu einer Verklemmung (engl. <i>Deadlock</i>) zwischen den nebenläufigen Threads kommen, die um den Zugriff auf die Ressourcen konkurrieren. Ein Deadlock bezeichnet den Zustand, bei dem eine zyklische Wartekette zwischen mehreren Threads eingetreten ist, wobei jeder beteiligte Thread auf die Freigabe von Sperren auf Ressourcen wartet, die ein anderer beteiligter Thread bereits exklusiv erlangt hat. Im einfachsten Fall sind wie in der folgenden Abbildung visualisiert nur 2 Threads und 2 Ressourcen beteiligt.</p>
<p>Wenn ein Thread nicht nur eine, sondern mehrere Sperren auf exklusiv nutzbare Ressourcen anfordert, kann es zu einer Verklemmung (engl. <i>Deadlock</i>) zwischen den nebenläufigen Threads kommen, die um den Zugriff auf die Ressourcen konkurrieren. Ein Deadlock bezeichnet den Zustand, bei dem eine zyklische Wartekette zwischen mehreren Threads eingetreten ist, wobei jeder beteiligte Thread auf die Freigabe von Sperren auf Ressourcen wartet, die ein anderer beteiligter Thread bereits exklusiv erlangt hat. Im einfachsten Fall sind, wie in der folgenden Abbildung visualisiert, nur 2 Threads und 2 Ressourcen beteiligt.</p>
<img src="media/concurrency_deadlock.png" style="width:800px">
<label>Verklemmung von Threads beim Zugriff auf exklusiv nutzbare Ressourcen</label>
......@@ -622,7 +622,7 @@ class BoundedBufferBlockingQueue implements BoundedBuffer {
<li><b>Concurrent Collections:</b> Neben Warteschlangen werden <code>Collection</code>-Implementierungen angeboten, die für die Verwendung im Multithreading-Kontext konzipiert sind, z.B. <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/ConcurrentHashMap.html"><code>ConcurrentHashMap</code></a> und <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/CopyOnWriteArrayList.html"><code>CopyOnWriteArrayList</code></a>. Wenn zu erwarten ist, dass viele Threads auf eine bestimmte Collection zugreifen, ist eine <code>ConcurrentHashMap</code> i.d.R. einer synchronisierten <code>HashMap</code> vorzuziehen. Eine <code>CopyOnWriteArrayList</code> ist einer synchronisierten <code>ArrayList</code> vorzuziehen, wenn die erwartete Anzahl an Lesezugriffen und Iterationen die Anzahl an Schreibzugriffen auf einer Liste übersteigt.</li>
<li><b>Locks:</b> Die Implementierungen der Interfaces <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/locks/Lock.html"><code>Lock</code></a> und <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/locks/Condition.html"><code>Condition</code></a> ermöglichen eine größere Flexibilität bei der Verwendung von Sperren und Bedingungen, jedoch auf Kosten einer unhandlicheren Syntax im Vergleich zum Schlüsselwort <code>synchronized</code>.</li>
<li><b>Timing:</b> Die Klasse <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/TimeUnit.html"><code>TimeUnit</code></a> bietet unterschiedliche Granularitäten (inkl. Nanosekunden) zum Steuern von Timeout-basierten Vorgängen.</li>
<li><b>Atomics:</b> Eine Sammlung von Klassen, die eine Thread-sichere Programmierung ohne Sperren für einzelne Variablen unterstützen, findet sich im Package <code>java.util.concurrent.atomic</code> – insbesondere für Variablen, die von einem Basisdatentyp sind. Instanzen der Klasse <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/atomic/AtomicLong.html"><code>AtomicLong</code></a> o.ä. bieten jeweils Zugriff und Aktualisierungen für eine einzelne Variable des entsprechenden Typs. Mittels <code>AtomicLong</code> kann im Multithreading-Kontext z.B. die Vergabe einer eindeutigen Sequenznummer realisiert, wie das folgende Code-Beispiel demonstriert.</li>
<li><b>Atomics:</b> Eine Sammlung von Klassen, die eine Thread-sichere Programmierung ohne Sperren für einzelne Variablen unterstützen, findet sich im Package <code>java.util.concurrent.atomic</code> – insbesondere für Variablen, die von einem Basisdatentyp sind. Instanzen der Klasse <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/atomic/AtomicLong.html"><code>AtomicLong</code></a> o.ä. bieten jeweils Zugriff und Aktualisierungen für eine einzelne Variable des entsprechenden Typs. Mittels <code>AtomicLong</code> kann im Multithreading-Kontext z.B. die Vergabe einer eindeutigen Sequenznummer realisiert werden, wie das folgende Code-Beispiel demonstriert.</li>
<pre><code class="language-java line-numbers">class Sequencer {
private final AtomicLong sequenceNumber = new AtomicLong(0);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment