Java - Reentrant Monitor
Hallo zusammen, zukünftige Java-Zauberer! Heute machen wir uns auf eine aufregende Reise in die Welt der Reentrant Monitors in Java. Machen Sie sich keine Sorgen, wenn Sie neu im Programmieren sind – ich werde Ihr freundlicher Führer sein, und wir werden dies Schritt für Schritt durchgehen. Also, holen Sie sich Ihre virtuellen Zauberstäbe (Tastaturen), und tauchen wir ein!
Was ist ein Reentrant Monitor?
Bevor wir uns den Details zuwenden, lassen Sie uns verstehen, was ein Reentrant Monitor ist. Stellen Sie sich vor, Sie sind in einer magischen Bibliothek, in der nur eine Person gleichzeitig in einen bestimmten Abschnitt eintreten kann. Was wäre, wenn Sie bereits in diesem Abschnitt sind und tiefer in einen Unterabschnitt gehen müssen? Ein Reentrant Monitor ist wie ein magischer Pass, der es Ihnen ermöglicht, genau das zu tun – in einen Abschnitt einzutreten, in dem Sie bereits sind!
In Java-Begriffen ermöglicht ein Reentrant Monitor einem Thread, der bereits einen Lock hält, diesen erneut zu erwerben, ohne blockiert zu werden. Es ist, als geben Sie sich die Erlaubnis, in einen Raum einzutreten, in dem Sie bereits sind. Cool, oder?
Warum brauchen wir Reentrant Monitors?
Vielleicht fragen Sie sich, "Warum brauchen wir diesen magischen Pass?" Nun, in der Welt der Multithreading (wo mehrere Teile eines Programms gleichzeitig laufen), müssen wir oft gemeinsame Ressourcen schützen. Reentrant Monitors helfen uns dabei effizienter zu arbeiten, insbesondere wenn wir Methoden haben, die andere Methoden aufrufen, die auch denselben Lock benötigen.
Einführung in ReentrantLock
Java stellt uns eine Klasse namens ReentrantLock
zur Verfügung, um Reentrant Monitors zu implementieren. Es ist wie unser magischer Bibliothekspass, aber in Codeform!
Syntax
So erstellen und verwenden wir einen ReentrantLock:
import java.util.concurrent.locks.ReentrantLock;
ReentrantLock lock = new ReentrantLock();
// Zum Sperren
lock.lock();
try {
// Ihr geschützter Code hier
} finally {
// Zum Entsperren
lock.unlock();
}
Machen Sie sich keine Sorgen, wenn das initially etwas einschüchternd aussieht. Wir werden es mit einigen Beispielen erklären!
Multithreading ohne Reentrant Lock
Lassen Sie uns mit einem einfachen Beispiel ohne Verwendung von ReentrantLock beginnen. Stellen Sie sich vor, wir haben einen magischen Zähler, den mehrere Zauberer (Threads) erhöhen möchten:
public class MagicalCounter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
Nun erstellen wir einige Zauberer-Threads, um diesen Zähler zu erhöhen:
public class WizardThread extends Thread {
private MagicalCounter counter;
public WizardThread(MagicalCounter counter) {
this.counter = counter;
}
public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}
}
public class MagicalCounterTest {
public static void main(String[] args) throws InterruptedException {
MagicalCounter counter = new MagicalCounter();
WizardThread wizard1 = new WizardThread(counter);
WizardThread wizard2 = new WizardThread(counter);
wizard1.start();
wizard2.start();
wizard1.join();
wizard2.join();
System.out.println("Endgültige Anzahl: " + counter.getCount());
}
}
Wenn Sie das ausführen, erwarten Sie vielleicht, dass die Endanzahl 2000 beträgt (1000 Erhöhungen von jedem Zauberer). Aber Überraschung! Das Ergebnis ist oft weniger als 2000. Dies liegt daran, dass unsere Zauberer aufeinander treten – sie versuchen gleichzeitig den Zähler zu erhöhen, was zu verlorenen Erhöhungen führt.
Multithreading mit Reentrant Lock
Nun, lassen Sie uns ein wenig ReentrantLock-Zauber auf unseren Zähler zaubern:
import java.util.concurrent.locks.ReentrantLock;
public class MagicalCounterWithLock {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
Lassen Sie uns das einmal auseinandernehmen:
- Wir erstellen ein
ReentrantLock
-Objekt namenslock
. - In der
increment
-Methode rufen wirlock.lock()
auf, bevor wir den Zähler erhöhen. - Wir verwenden einen try-finally-Block, um sicherzustellen, dass wir immer entsperrt werden, auch wenn eine Ausnahme auftritt.
- Nach dem Erhöhen rufen wir
lock.unlock()
im finally-Block auf.
Wenn wir unseren WizardThread-Test mit diesem neuen MagicalCounterWithLock ausführen, werden wir immer 2000 als Endanzahl erhalten. Unsere Zauberer nehmen jetzt nett hintereinander auf!
Multithreading mit Reentrant Lock als Fair
ReentrantLock hat noch einen Trick im Ärmel. Wir können es mit einem Fairness-Parameter erstellen:
ReentrantLock fairLock = new ReentrantLock(true);
Wenn wir Fairness auf true setzen, bevorzugt der Lock den Zugang zur longest-waiting-Thread. Es ist, als würden wir eine richtige Warteschlange für unsere Zauberer bilden!
So könnten wir es verwenden:
public class FairMagicalCounter {
private int count = 0;
private ReentrantLock fairLock = new ReentrantLock(true);
public void increment() {
fairLock.lock();
try {
count++;
} finally {
fairLock.unlock();
}
}
public int getCount() {
return count;
}
}
Dies stellt sicher, dass, wenn mehrere Zauberer auf das Erhöhen des Zählers warten, derjenige, der am längsten gewartet hat, als nächstes dran ist.
Fazit
Und das war's, junge Zauberer! Wir haben die magische Welt der Reentrant Monitors in Java durchquert. Wir haben gesehen, wie sie uns dabei helfen, gemeinsame Ressourcen in Multithreading-Umgebungen zu verwalten und sicherzustellen, dass unsere magischen Zähler (und andere gemeinsame Objekte) korrekt erhöht werden.
Denken Sie daran, dass Reentrant Monitors wie jede mächtige Magie klug verwendet werden sollten. Sie sind großartig für die Verwaltung.concurrenter Zugang zu gemeinsamen Ressourcen, aber Missbrauch kann zu vermindertem Leistungsgewinn oder sogar Deadlocks führen (eine Situation, in der Zauberer ewig aufeinander warten!).
Üben Sie diese Zauber... ähm, Codebeispiele, und bald werden Sie Multithreading-Zauber wie ein Profi zaubern! Frohes Coden und möge Ihre Threads immer im Einklang sein!
Credits: Image by storyset