Threadsichere verzögerte Initialisierung in Java

Shubham Vora 12 Oktober 2023
  1. Objektinitialisierung in Java
  2. Implementieren Sie Lazy Initialization in Java
  3. Verwenden Sie die synchronized-Methode für Thread-Safe Lazy Initialization in Java
  4. Verwenden Sie die Double-Checked-Sperrmethode für Thread-sichere Lazy-Initialisierung in Java
Threadsichere verzögerte Initialisierung in Java

In diesem Artikel wird die Implementierung einer Thread-sicheren Lazy-Initialisierung in Java erläutert.

Objektinitialisierung in Java

Verzögerte Initialisierung ist die Verzögerung der Objekterstellung. Es kann auch zum ersten Mal zu Verzögerungen bei einigen Berechnungsaufgaben oder teuren Prozessen kommen.

Es gibt zwei Arten der Objektinitialisierung in Java. Dies sind die Eager-Initialisierung und die Lazy-Initialisierung.

Die Objektinitialisierung erfolgt während der Kompilierzeit in der Eager-Initialisierung. Bei einem langwierigen Prozess ist dies zeit- und speicherintensiv.

Bei der verzögerten Initialisierung erfolgt die Objektinitialisierung, wenn das Programm sie benötigt. So spart es Speicher, erhöht die Verarbeitungsleistung und verbessert die Effizienz.

Threads sind kleinere Komponenten, die Prozesse parallel ausführen können, um die Zeit zu optimieren. Thread Safe ist eine Java-Klasse, die die Korrektheit des internen Zustands der Klasse und der Werte sicherstellt, die die Funktionen während mehrerer Thread-Aufrufe gleichzeitig zurückgeben.

Implementieren Sie Lazy Initialization in Java

Eine Getter-Methode prüft, ob ein privates Element bereits einen Wert hat. Wenn dies der Fall ist, gibt die Funktion es zurück; Andernfalls wird eine neue Instanz erstellt und bei der erstmaligen Ausführung zurückgegeben.

Es gibt zwei Methoden, um eine verzögerte Initialisierung durchzuführen.

  1. Verwenden Sie das Schlüsselwort synchronize vor dem Funktionsnamen.
  2. Verwenden Sie die doppelt geprüfte Verriegelung oder die synchronisierte Blockmethode.

Schreiben Sie Thread-sicheren Code für verzögerte Initialisierung in Java

Eine Variable kann unerwartete Ergebnisse zurückgeben, wenn mehrere Threads parallel ausgeführt werden. Hier brauchen wir einen thread-safe Code, um Deadlocks in Java zu verhindern.

Beispiel:

Stellen Sie sich eine Bank vor, bei der nur ein Token-Zähler für die Token-Verteilung verfügbar ist. Die Aufgabe hier besteht darin, nur dann eine Klasse Bank zu erstellen, wenn ein Kunde hereinkommt; Der Prozess ist eine verzögerte Initialisierung.

Lassen Sie uns mehrere Threads erstellen, sagen wir t1, t2 und so weiter, um mehrere Thread-Aufrufe zu verstehen. Jeder Thread wird ein Kunde sein.

Wenn ein Kunde wegen des Tokens kommt, erstellen wir ein Bank-Objekt und rufen operation() mit der Token-Nummer im Funktionsargument auf. Zeigen Sie nun an, ob ein Token verfügbar ist, und geben Sie die nächste Aktion in der Nachricht an.

Wenn das Token verfügbar ist, geben Sie das Token aus; Fahren Sie andernfalls mit dem nächsten Kunden fort.

Das Programm erklärt sowohl den getInstanceSynchronizedWay() als auch den getInstanceSynchronizedBlockWay(). Wir können Thread.sleep verwenden, um die Ausgabegenauigkeit sicherzustellen.

Verwenden Sie die synchronized-Methode für Thread-Safe Lazy Initialization in Java

In diesem Beispiel haben wir zwei Kunden-Threads.

Die Variable _instance prüft, ob eine Instanz null ist. Wenn die Variable null ist, erstellt das Programm eine Instanz.

Das Leer-Flag prüft, ob der Token verfügbar ist oder nicht. Wenn das Token verfügbar ist, ist der Flag-Wert wahr.

Nach der Übergabe des Tokens an den Kunden setzt das Programm den Flag-Wert auf false. Der nächste Kunde erhält den Token also erst, wenn die operation() abgeschlossen ist.

Beispielcode:

// Helper class as a Singleton Class
class BankOperation {
  // Private variables
  private static BankOperation _instance;
  private boolean empty = false;
  private String customerName = "default";
  // Displays the instance only during creation
  private BankOperation() {
    System.out.println("Instance Creation Over\n");
  }
  // synchronized method
  public static synchronized BankOperation getInstanceSynchronizedWay() {
    if (_instance == null)
      _instance = new BankOperation();
    return _instance;
  }
  // Check if the token is available
  public boolean isOperationBankEmpty() {
    return empty;
  }
  // When token giving is successful
  public void endOperation() {
    empty = true;
  }
  // Multiple threads access the method below
  public synchronized void operation(String paramCust) {
    // When the token is available. The flag is true.
    if (empty == true) {
      customerName = paramCust;
      // Issue the token to the customer
      System.out.println("Operation - Token is available.\n"
          + "Giving token to - " + customerName);
      empty = false;
    }
    // The token is not available
    else {
      System.out.println("Sorry " + paramCust + ", Counter closed with " + customerName);
    }
  }
}
// Main class
public class Bank {
  // Driver function
  public static void main(String args[]) {
    // synchronized method
    // Create a thread in main()
    Thread t1 = new Thread(new Runnable() {
      // run() for thread 1
      public void run() {
        // Create objects of other classes here

        BankOperation i1 = BankOperation.getInstanceSynchronizedWay();

        System.out.println("Synchronized Method - Instance 1 - " + i1 + "\n");
        // The method with an argument
        i1.endOperation();
        i1.operation("Customer 1");
      }
    });
    // Thread 2
    Thread t2 = new Thread(new Runnable() {
      // run() for thread 2
      public void run() {
        BankOperation i2 = BankOperation.getInstanceSynchronizedWay();
        System.out.println("Synchronized Method - Instance 2 - " + i2 + "\n");
        i2.operation("Customer 2");
      }
    });
    // Starting thread 1
    t1.start();
    // Start thread 2
    t2.start();
  }
}

Ausgang:

Instance Creation Over
Synchronized Method - Instance 1 - BankOperation@792bbbb1
Synchronized Method - Instance 2 - BankOperation@792bbbb1
Operation - Token is available.
Giving the token to - Customer 1
Sorry Customer 2, Counter closed with Customer 1

In der obigen Ausgabe können Benutzer beobachten, dass zwei Kundeninstanzen parallel laufen. Die erste Instanz erhält also das Token und die zweite Instanz erhält eine Nachricht.

Verwenden Sie die Double-Checked-Sperrmethode für Thread-sichere Lazy-Initialisierung in Java

In diesem Beispiel erstellen wir zwei Kunden-Threads. Die Variable _instanceForDoubleCheckLocking prüft zweimal, ob eine Instanz null ist, und die Methode getInstanceSynchronizedBlockWay() gibt die neue Instanz zurück.

Der Wert des Flags leer bestimmt die Token-Verfügbarkeit. Der Token steht dem Kunden zur Verfügung, wenn das Flag wahr ist.

Das Flag wird falsch, nachdem der Kunde das Token erhalten hat. Das Ausführen mehrerer Threads kann keinen Wert ändern, bis der aktuelle Thread seine Operation beendet.

Beispielcode:

// Helper class
class BankOperation {
  // Private variable declaration
  private static BankOperation _instanceForDoubleCheckLocking;
  private boolean empty = false;
  private String customerName = "default";
  private BankOperation() {
    System.out.println("Instance Creation Over\n");
  }
  // Synchronized Block Method or Double-Checked Locking
  public static BankOperation getInstanceSynchronizedBlockWay() {
    // Check double locking

    if (_instanceForDoubleCheckLocking == null)
      synchronized (BankOperation.class) {
        if (_instanceForDoubleCheckLocking == null)
          _instanceForDoubleCheckLocking = new BankOperation();
      }
    return _instanceForDoubleCheckLocking;
  }
  // The `token` availability check
  public boolean isOperationBankEmpty() {
    return empty;
  }
  // After giving the token set the flag value
  public void endOperation() {
    empty = true;
  }
  // Multiple threads access the method below
  public synchronized void operation(String paramCust) {
    // The flag is true when the `token` is available
    if (empty == true) {
      customerName = paramCust;
      // Give the `token` to the customer
      System.out.println("Operation - Token is available.\n"
          + "Giving token to - " + customerName);
      empty = false;
    }
    // When the `Token` is not available
    else {
      System.out.println("Sorry " + paramCust + ", Counter closed with " + customerName);
    }
  }
}
// Main class
public class Bank {
  // Driver function
  public static void main(String args[]) {
    // Double Checked Locking
    System.out.println("Double Checked locking - Synchronized Block");
    // Thread 3
    Thread t3 = new Thread(new Runnable() {
      // run() for thread 3
      public void run() {
        BankOperation i1 = BankOperation.getInstanceSynchronizedBlockWay();
        System.out.println("Double Checked Locking - Instance 1 - " + i1 + "\n");

        i1.endOperation();
        i1.operation("Customer 1");
      }
    });
    // Thread 4
    Thread t4 = new Thread(new Runnable() {
      // run() for thread 4
      public void run() {
        BankOperation i2 = BankOperation.getInstanceSynchronizedBlockWay();
        System.out.println("Double Checked Locking - Instance 2 - " + i2 + "\n");
        i2.operation("Customer 2");
      }
    });
    t3.start();
    t4.start();
  }
}

Ausgang 1:

Double Checked locking - Synchronized Block
Instance Creation Over
Double Checked Locking - Instance 1 - BankOperation@1efc89d6
Double Checked Locking - Instance 2 - BankOperation@1efc89d6
Operation - Token is available.
Giving  token to - Customer 1
Sorry Customer 2, Counter closed with Customer 1

Beachten Sie, dass, wenn zwei Kundeninstanzen parallel eintreffen, das Programm das Token an die erste Instanz liefert und den zweiten Kunden benachrichtigt.

Ausgang 2:

Double Checked locking - Synchronized Block
Instance Creation Over
Double Checked Locking - Instance 2 - BankOperation@282416b6
Double Checked Locking - Instance 1 - BankOperation@282416b6
Sorry Customer 2, the counter closed with the default
Operation - Token is available.
Giving Bank token to - Customer 1

Hier ist der Token für Kunde 2 nicht verfügbar, weil die endOperation() vor der operation() fehlt. Kunde 1 bekommt den Token, weil die endOperation() vor der operation() läuft.

Dieses Tutorial hat uns zwei Methoden beigebracht, um eine Thread-sichere Lazy-Initialisierung in Java zu implementieren. Die erste Methode verwendet das Schlüsselwort synchronized und die zweite Option ist die synchronisierte Blockmethode.

Die synchronisierte Blockmethode sorgt für eine doppelte Kontrolle. Benutzer können basierend auf dem Kontext einen beliebigen Weg wählen.

Shubham Vora avatar Shubham Vora avatar

Shubham is a software developer interested in learning and writing about various technologies. He loves to help people by sharing vast knowledge about modern technologies via different platforms such as the DelftStack.com website.

LinkedIn GitHub