Java의 스레드 안전 지연 초기화

Shubham Vora 2023년10월12일
  1. Java의 객체 초기화
  2. Java에서 지연 초기화 구현
  3. Java에서 스레드 안전 지연 초기화를 위한 synchronized 방법 사용
  4. Java에서 스레드로부터 안전한 지연 초기화를 위해 이중 확인 잠금 방법 사용
Java의 스레드 안전 지연 초기화

이 기사에서는 Java에서 스레드 안전 지연 초기화 구현에 대해 설명합니다.

Java의 객체 초기화

지연 초기화는 객체 생성을 지연시키는 행위입니다. 또한 처음으로 일부 계산 작업이나 비용이 많이 드는 프로세스에서 지연이 발생할 수 있습니다.

Java에는 두 가지 유형의 개체 초기화가 있습니다. Eager 초기화와 Lazy 초기화가 있습니다.

개체 초기화는 Eager 초기화에서 컴파일 시간 동안 발생합니다. 긴 프로세스의 경우 시간과 메모리를 많이 소모합니다.

지연 초기화에서 개체 초기화는 프로그램에서 필요할 때 발생합니다. 따라서 메모리를 절약하고 처리 능력을 높이며 효율성을 향상시킵니다.

스레드는 시간을 최적화하기 위해 프로세스를 병렬로 실행할 수 있는 보조 구성 요소입니다. Thread Safe는 동시에 여러 스레드 호출 중에 함수가 반환하는 클래스의 내부 상태 및 값의 정확성을 보장하는 Java 클래스입니다.

Java에서 지연 초기화 구현

getter 메서드는 개인 멤버에 이미 어떤 값이 있는지 확인합니다. 있는 경우 함수는 이를 반환합니다. 그렇지 않으면 새 인스턴스를 만들고 처음 실행 시 반환합니다.

지연 초기화를 수행하는 방법에는 두 가지가 있습니다.

  1. 함수 이름 앞에 synchronize 키워드를 사용하십시오.
  2. 이중 확인 잠금 또는 동기화 차단 방법을 사용하십시오.

Java에서 지연 초기화를 위한 스레드 안전 코드 작성

여러 스레드가 병렬로 실행될 때 변수가 예기치 않은 결과를 반환할 수 있습니다. 여기에서 Java의 교착 상태를 방지하기 위해 스레드 안전 코드가 필요합니다.

예:

토큰 배포에 사용할 수 있는 토큰 카운터가 하나만 있는 은행을 고려하십시오. 여기서 작업은 고객이 들어올 때만 은행 클래스를 만드는 것입니다. 프로세스는 게으른 초기화입니다.

다중 스레드 호출을 이해하기 위해 t1, t2 등과 같은 다중 스레드를 생성해 보겠습니다. 각 스레드는 고객이 됩니다.

고객이 토큰을 찾으러 오면 Bank 개체를 만들고 함수 인수의 토큰 번호로 operation()을 호출합니다. 이제 토큰을 사용할 수 있는지 여부를 표시하고 메시지에서 다음 작업을 지정합니다.

토큰을 사용할 수 있으면 토큰을 발행하십시오. 그렇지 않으면 다음 고객에게 진행합니다.

프로그램은 getInstanceSynchronizedWay()getInstanceSynchronizedBlockWay()를 모두 설명합니다. 출력 정확도를 보장하기 위해 Thread.sleep을 사용할 수 있습니다.

Java에서 스레드 안전 지연 초기화를 위한 synchronized 방법 사용

이 예에는 두 개의 고객 스레드가 있습니다.

_instance 변수는 인스턴스가 null인지 확인합니다. 변수가 null이면 프로그램이 인스턴스를 만듭니다.

빈 플래그는 토큰이 사용 가능한지 여부를 확인합니다. 토큰을 사용할 수 있으면 플래그 값은 true입니다.

고객에게 토큰을 제공한 후 프로그램은 플래그 값을 false로 설정합니다. 따라서 다음 고객은 operation()이 완료될 때까지 토큰을 받지 못합니다.

예제 코드:

// 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();
  }
}

출력:

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

위의 출력에서 사용자는 두 개의 고객 인스턴스가 병렬로 제공되는 것을 관찰할 수 있습니다. 따라서 첫 번째 인스턴스는 토큰을 받고 두 번째 인스턴스는 메시지를 받습니다.

Java에서 스레드로부터 안전한 지연 초기화를 위해 이중 확인 잠금 방법 사용

이 예에서는 두 개의 고객 스레드를 생성합니다. _instanceForDoubleCheckLocking 변수는 인스턴스가 null인지 여부를 두 번 확인하고 getInstanceSynchronizedBlockWay() 메서드는 새 인스턴스를 반환합니다.

비어 있음 플래그의 값은 토큰 가용성을 결정합니다. 플래그가 true이면 고객이 토큰을 사용할 수 있습니다.

고객이 토큰을 받은 후 플래그는 false가 됩니다. 여러 스레드를 실행하면 현재 스레드가 작업을 완료할 때까지 값을 변경할 수 없습니다.

예제 코드:

// 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();
  }
}

출력 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

두 고객 인스턴스가 병렬로 도착하면 프로그램이 첫 번째 인스턴스에 토큰을 제공하고 두 번째 고객에게 알립니다.

출력 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

여기서 고객 2는 토큰을 사용할 수 없으며 그 이유는 operation() 이전에 endOperation()이 누락되었기 때문입니다. 고객 1은 endOperation()operation() 이전에 실행되기 때문에 토큰을 얻습니다.

이 자습서에서는 Java에서 스레드로부터 안전한 지연 초기화를 구현하는 두 가지 방법을 배웠습니다. 첫 번째 방법은 synchronized 키워드를 사용하고 두 번째 옵션은 동기화된 차단 방법입니다.

동기화된 차단 방법은 이중 확인을 보장합니다. 사용자는 컨텍스트에 따라 한 가지 방법을 선택할 수 있습니다.

작가: Shubham Vora
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