Semaphor ist eine Klasse, die üblicherweise im JUC -Paket verwendet wird. Es handelt sich um eine Anwendung des AQS Sharing -Modus. Es können mehrere Threads gleichzeitig mit gemeinsam genutzten Ressourcen arbeiten und die Anzahl der Parallelität effektiv steuern. Es kann eine gute Verkehrskontrolle erreichen. Semaphore bietet ein Konzept einer Lizenz, die als Buskarte angesehen werden kann. Nur diejenigen, die das Ticket erfolgreich erhalten, können in den Bus steigen. Es gibt eine bestimmte Anzahl von Tickets, und es ist unmöglich, sie ohne Einschränkungen auszustellen, was zu einer Überladung des Busses führt. Wenn das Ticket ausgestellt wird (der Bus ist voll), können die anderen nur auf den nächsten Zug warten. Wenn jemand auf halbem Weg aus dem Bus steigt, ist seine Position kostenlos. Wenn andere zu diesem Zeitpunkt in den Bus steigen möchten, können sie wieder Tickets bekommen. Verschiedene Pools können mit Semaphor implementiert werden. Am Ende dieses Artikels schreiben wir einen einfachen Datenbankverbindungspool. Schauen wir uns zunächst den Konstruktor des Semaphors an.
// Konstruktor 1Public Semaphor (int Genehmigungen) {sync = new Nonfairsync (Genehmigungen);} // Konstruktor 2Public Semaphor (int Genehmigungen, boolean fair) {sync = fair? NEUE FAIRSYNC (Genehmigungen): Neue Nicht -Fairsync (Genehmigungen);}Semaphor liefert zwei parameterfreie Konstruktoren, es werden jedoch keine parameterfreien Konstruktoren bereitgestellt. Beide Konstruktoren müssen eine anfängliche Anzahl von Lizenzen übergeben. Das mit dem Konstruktor 1 konstruierte Semaphor wird bei der Erhalt der Lizenz nicht zutreffend erhalten. Durch die Verwendung von Konstruktor 2 kann die Methode zum Erhalten der Lizenz durch Parameter (fair oder unfair) angegeben werden. Semaphore stellt der Außenwelt hauptsächlich zwei Arten von APIs zur Verfügung, um Lizenzen zu erhalten und Lizenzen zu veröffentlichen. Der Standardwert besteht darin, eine Lizenz zu erhalten und zu veröffentlichen, und Parameter können auch übergeben werden, um mehrere Lizenzen gleichzeitig zu erhalten und zu veröffentlichen. In diesem Artikel werden wir nur über die Situation sprechen, jedes Mal eine Lizenz zu erhalten und zu veröffentlichen.
1. Erhalten Sie eine Lizenz
//Get a license (response interrupt) public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1);}//Get a license (not responding to interrupt) public void acquireUninterruptibly() { sync.acquireShared(1);}//Try to obtain a license (nonfair acquisition) public boolean tryAcquire() { return sync.nonfairtryacquiresharedared (1)> = 0;} // Versuchen Sie, eine Lizenz zu erhalten (langfristig, Zeiteinheit), die InterruptedException {return sync.tryacquiresharednanos (1, Einheit.Tonanos (Auszeit);};}Die obige API ist die von Semaphore bereitgestellte Standardlizenzerwerbs. Nur eine Lizenz zu einem Zeitpunkt zu erhalten, ist auch eine gemeinsame Situation im wirklichen Leben. Zusätzlich zum direkten Abrufen bietet es auch Versuch zum Abrufen. Der direkte Abrufvorgang kann den Thread nach dem Ausfall blockieren, während der Versuch, das Abruf zu holen, nicht. Es sollte auch beachtet werden, dass die Tryacquire -Methode verwendet wird, um sie auf unfaire Weise zu erhalten. Was wir in normalen Zeiten oft verwenden, ist, eine Lizenz zu erhalten. Schauen wir uns an, wie es erhalten wird. Sie können sehen, dass die Erwerbsmethode direkt synchronisiert wird. Diese Methode ist die Methode in AQs. Wir haben einmal über die Artikel der AQS Source Code Series gesprochen. Lassen Sie es uns erneut überprüfen.
// Erwerben der Sperre im unterbrechungsfähigen Modus (Shared -Modus) öffentliche endgültige void erwerben, die er Akku (int arg) unterruptedException auswirkt {// Bestimmen Sie zuerst, ob der Thread unterbrochen wird, wenn ja, eine Ausnahme, wenn (thread.interrupted ()) {Neue InterruptedException (); } // 1. Versuchen Sie, das Schloss zu erwerben, wenn (tryacquireshared (arg) <0) {// 2. Wenn die Akquisition fehlschlägt, geben Sie die Methode doacquiresharedinterriptable (ARG) ein. }}Die erste Methode, die erwerben, besteht darin, die tryAcquireshared -Methode aufzurufen, um zu versuchen, sie zu erhalten. Tryacquireshared ist eine abstrakte Methode in AQs. Die beiden abgeleiteten Klassen fairsync und nicht fairsync implementieren die Logik dieser Methode. Fairsync implementiert die Logik des fairen Erwerbs, während nicht fairsync die Logik des Nicht-Fair-Erwerbs implementiert.
Abstract statische Klasse Sync erweitert AbstractQueuedsynchronizer {// Versuchen Sie, die endgültige int nicht fairtryacquireshared (int zu erwerben) {für (;;) {// verfügbare Lizenzen int verfügbar = getState (); // verbleibende Lizenzen int verbleiben = verfügbar - erwirbt; // 1. Wenn Sie weniger als 0 verbleiben, geben Sie die verbleibende direkte // 2 zurück. Wenn Sie größer als 0 sind, aktualisieren Sie zuerst den Synchronisationsstatus und geben Sie dann die verbleibende zurück, wenn (verbleibend <0 || vergleicheStetState (verfügbar, verbleibend)) {return verbleibend; }}}} // nicht fairsync statische endgültige Klasse Nonfairsync erweitert Sync {private statische endgültige long serialversionuid = -2694183684443567898l; Nicht fairsync (int genehmigt) {super (genehmigt); } // Versuchen Sie, eine lizenzgeschützte Int -Tryacquireshared (int zu erwerben) {Nonfairtryacquireshared (erwirbt) zurück; }} // Fair Synchronizer statische endgültige Klasse Fairsync erweitert Sync {private statische endgültige lange Serialversionuid = 2014338818796000944L; FairSync (int genehmigt) {Super (Genehmigungen); } // Versuchen Sie, die lizenzge. } // verfügbare Lizenzen int verfügbar = getState (); // Die verbleibenden Lizenzen int verbleiben = verfügbar - erwerben; // 1. Wenn Sie weniger als 0 verbleiben, kehren Sie direkt zum verbleibenden // 2 zurück. Wenn der verbleibende Wert größer als 0 ist, wird der Synchronisationsstatus zuerst aktualisiert und dann an die verbleibenden zurückgegeben, wenn (verbleibend <0 || vergleicheSetState (verfügbar, verbleibend)) {return verbleibend; }}}}Es ist hier zu beachten, dass die tryAcquireshared -Methode der nicht fairen Synchronisation die nicht fairtryacquireshared -Methode direkt aufruft, die sich in der Synchronisierung der Elternklasse befindet. Die Logik der Nicht-Fair-Erfassungsschloss besteht darin, zunächst den aktuellen Synchronisationszustand (Synchronstatus darzustellen die Anzahl der Lizenzen) und den Parameter des aktuellen Synchronisationszustands subtrahieren. Wenn das Ergebnis nicht weniger als 0 ist, wird nachgewiesen, dass noch Lizenzen verfügbar sind, der Wert des Synchronisationszustands wird direkt unter Verwendung des CAS -Betriebs aktualisiert. Schließlich wird der Ergebniswert zurückgegeben, unabhängig davon, ob das Ergebnis kleiner als 0 ist. Hier müssen wir die Bedeutung des Rückgabewerts der Tryacquireshared -Methode verstehen. Rückgabe einer negativen Zahl bedeutet, dass die Akquisition fehlgeschlagen ist. Schauen wir uns den Code der erwerbenen Methode an.
// Erwerben von Sperren im unterbruptiblen Modus (Shared -Modus) öffentliche endgültige void erwerben, die er Akku (int arg) unterbrußt. } // 1. Versuchen Sie, die Sperre zu erwerben // Negative Zahl: Zeigt an, dass die Akquisition fehlgeschlagen ist // Nullwert: Gibt an, dass der aktuelle Thread erfolgreich erfasst wird, der nachfolgende Thread jedoch nicht mehr erhalten kann // positive Zahl: Gibt an, dass der aktuelle Thread erfolgreich erfasst wird, und der nachfolgende Thread kann auch einen Erfolg erhalten, wenn (tryacquireshared (arg) <0) {// 2. Wenn die Akquisition fehlschlägt, geben Sie die Methode doacquiresharedinterriptable (ARG) ein. }}Wenn die verbleibende zurückgegebene beträgt weniger als 0 ist, bedeutet dies, dass die Akquisition fehlgeschlagen ist. Daher ist Tryacquireshared (arg) <0 wahr, sodass die Methode doacquiresharedinterruptibel als nächstes aufgerufen wird. Wenn wir über AQs sprachen, wickelt er den aktuellen Thread in einen Knoten ein und steckt ihn in den Schwanz der Synchronisationswarteschlange, und es ist möglich, den Faden auszusetzen. Dies ist auch der Grund, warum Threads in der Schlange stehen und blockieren, wenn sie weniger als 0 verbleiben. Wenn der zurückgegebene verbleibende> = 0 bedeutet, dass der aktuelle Thread erfolgreich erfasst wurde. Daher ist TryAcquiresharedared (arg) <0 eine Flase, sodass die Methode doacquiresharedinterruptibel nicht mehr aufgerufen wird, um den aktuellen Thread zu blockieren. Das obige ist die gesamte Logik des unfairen Erwerbs. Bei der fairen Akquisition müssen Sie nur die Methode der HasqueuepreedPred -Anbieter anrufen, um festzustellen, ob sich jemand in der Synchronisationswarteschlange anstellt. In diesem Fall gibt die Rückgabe -1 direkt an, dass die Akquisition fehlgeschlagen ist, andernfalls werden die folgenden Schritte als unlauterer Erwerb fortgesetzt.
2. Veröffentlichen Sie die Lizenz
// Veröffentlichung einer Lizenz public void release () {sync.releaseshared (1);}Wenn Sie die Release -Methode aufrufen, werden eine Lizenz veröffentlicht. Sein Betrieb ist sehr einfach, daher nennen wir die freisetzende Methode von AQs. Schauen wir uns diese Methode an.
// Veröffentlichungsverriegelungsbetrieb (Shared -Modus) öffentliches endgültiges booleaner Booleaner (int arg) {// 1. Versuchen Sie, die Sperre zu veröffentlichen, wenn (tryReleaseshared (arg)) {// 2. Wenn die Veröffentlichung erfolgreich ist, wecken Sie andere Threads Doreleaseshared () auf; zurückkehren; } return false;}Die freigelassene Methode von AQS ruft zunächst die tryReleaSeshared -Methode auf, um zu versuchen, das Schloss freizugeben. Die Implementierungslogik dieser Methode befindet sich in der Unterklasse -Synchronisierung.
Abstract statische Klasse Sync erweitert AbstractQueuedsynchronizer {... // Versuchen Sie, den operativ geschützten endgültigen boolean tryreleaseshared (int releases) {für (;;) {// den aktuellen Synchronisationszustand int Current = getStstate () abrufen; // plus der aktuelle Synchronisationszustand wie folgt int next = current + releases; // Wenn das Additionsergebnis geringer ist als der aktuelle Synchronisationszustand, wird ein Fehler gemeldet, wenn (nächstes <Strom) {Neuen Fehler werfen ("Maximum -Genehmigungszahl überschritten"); } // Aktualisieren Sie den Wert des Synchronisationsstatus im CAS -Modus und geben Sie true zurück, wenn das Update erfolgreich ist. Andernfalls werden weiterhin geschoben, wenn (vergleicheSetState (aktuell, next)) {return true; }}} ...}Sie können sehen, dass die tryReleasesharedared -Methode eine für die Schleife zum Drehen verwendet. Erstigen Sie den Synchronisationszustand, fügen Sie zuerst die eingehenden Parameter hinzu und aktualisieren Sie dann den Synchronisationszustand in CAS. Wenn das Update erfolgreich ist, kehren Sie True zurück und springen Sie aus der Methode. Andernfalls wird die Schleife fortgesetzt, bis sie erfolgreich ist. Dies ist der Prozess der Veröffentlichung der Lizenz von Semaphore.
3. Schreiben Sie einen Verbindungspool manuell
Der Semaphorcode ist nicht sehr kompliziert. Die häufig verwendete Operation besteht darin, eine Lizenz zu erhalten und freizugeben. Die Implementierungslogik dieser Operationen ist relativ einfach, aber dies behindert die weit verbreitete Anwendung des Semaphors nicht. Als nächstes werden wir Semaphore verwenden, um einen einfachen Datenbankverbindungspool zu implementieren. In diesem Beispiel hoffen wir, dass die Leser ein tieferes Verständnis für die Verwendung von Semaphor haben können.
öffentliche Klasse ConnectPool {// Verbindungspool Größe private int Größe; // Datenbankverbindung Sammlung private Connect [] Connects; // Verbindungsstatus Flag private boolean [] ConnectFlag; // verbleibende Anzahl verfügbarer Anschlüsse private volatile int verfügbar; // Semaphore Private Semaphore Semaphore; // Constructor Public ConnectPool (int Größe) {this.size = Größe; this.Avableable = Größe; Semaphore = neues Semaphor (Größe, True); Connects = New Connect [Größe]; ConnectFlag = New Boolean [Größe]; initconnects (); } // Initialisieren Sie die Verbindung private void initconnects () {// generieren Sie eine angegebene Anzahl von Datenbankverbindungen für (int i = 0; i <this.size; i ++) {Connects [i] = new Connect (); }} // Datenbankverbindung erhalten private synchronisierte Connect getConnect () {für (int i = 0; i <connectFlag.length; i ++) {// Übertragen Sie die Sammlung, um nicht verwendete Verbindungen zu finden, wenn (! ConnectFlag [i]) {// die Verbindung so festlegen, dass sie in Verwendung von ConnectFlag [i] = true; // die Anzahl der verfügbaren Verbindungen abziehen-; System.out.println ("【"+Thread.currentThread (). GetName ()+"】, um die Anzahl der verbleibenden Verbindungen zu erhalten:"+verfügbar); // Rückgabe der Verbindungsreferenzrückgabe verbindet [i]; }} return null; } // eine Verbindung public connect openConnect () löscht InterruptedException {// lizenz semaphore.acquire (); // Datenbankverbindung return getConnect (); } // eine Verbindung öffentliche synchronisierte void -Release veröffentlichen (Connect Connect) {für (int i = 0; i <this.size; i ++) {if (Connect == Connects [i]) {// Setzen Sie die Verbindung so, dass sie nicht gebraucht werden [i] = false; // 1 verfügbare Verbindungsnummer hinzufügen; System.out.println ("【"+thread.currentThread (). GetName ()+"] um die verbleibende Verbindungsnummer zu veröffentlichen:"+verfügbar); // Lizenz Semaphore.Release () veröffentlichen; }}} // Anzahl der verfügbaren Verbindungen public int verfügbar () {return verfügbar; }}Testcode:
public class testThread erweitert Thread {private static ConnectPool Pool = new ConnectPool (3); @Override public void run () {try {connect connect = pool.openconnect (); Thread.Sleep (100); // einen Break -Pool nehmen. Release (Connect); } catch (interruptedException e) {e.printstacktrace (); }} public static void main (String [] args) {für (int i = 0; i <10; i ++) {new TestThread (). start (); }}}Testergebnisse:
Wir verwenden ein Array, um Referenzen auf Datenbankverbindungen zu speichern. Bei der Initialisierung des Verbindungspools werden wir die InitConnects -Methode aufrufen, um eine bestimmte Anzahl von Datenbankverbindungen zu erstellen und ihre Referenzen im Array zu speichern. Darüber hinaus gibt es ein Array der gleichen Größe, um festzustellen, ob die Verbindung verfügbar ist. Wenn ein externer Thread anfordert, eine Verbindung zu erhalten, rufen Sie zuerst die Methode semaphor.acquire () an, um eine Lizenz zu erhalten, und setzen Sie den Verbindungsstatus für die Verwendung und geben Sie schließlich die Verweise auf die Verbindung zurück. Die Anzahl der Lizenzen wird durch die während des Baus übergebenen Parameter bestimmt. Die Anzahl der Lizenzen wird jedes Mal um 1 reduziert, wenn die Methode Semaphor.acquire () aufgerufen wird. Wenn die Zahl auf 0 reduziert wird, bedeutet dies, dass keine Verbindung verfügbar ist. Zu diesem Zeitpunkt wird es blockiert, wenn andere Themen es wieder bekommen. Immer wenn ein Thread eine Verbindung veröffentlicht, wird Semaphore.Release () aufgerufen, um die Lizenz zu veröffentlichen. Zu diesem Zeitpunkt steigt die Gesamtzahl der Lizenzen erneut, was bedeutet, dass die Anzahl der verfügbaren Verbindungen steigt. Dann wacht der zuvor blockierte Thread auf und erhalten weiterhin die Verbindung. Zu diesem Zeitpunkt können Sie die Verbindung erfolgreich erhalten, indem Sie sie erneut erhalten. Im Testbeispiel wird ein Verbindungspool von 3 Verbindungen initialisiert. Aus den Testergebnissen können wir sehen, dass wenn ein Thread eine Verbindung erhält, die Anzahl der verbleibenden Verbindungen um 1 reduziert wird. Wenn sich der Faden auf 0 reduziert, können andere Fäden sie nicht mehr erhalten. Zu diesem Zeitpunkt müssen Sie darauf warten, dass ein Thread die Verbindung freigibt, bevor Sie sie fortsetzen. Sie können sehen, dass sich die Anzahl der verbleibenden Verbindungen immer zwischen 0 und 3 ändert, was bedeutet, dass unser Test erfolgreich war.
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.