Development Tip

게터와 세터를 동기화해야합니까?

yourdevel 2020. 12. 30. 19:43
반응형

게터와 세터를 동기화해야합니까?


private double value;

public synchronized void setValue(double value) {
    this.value = value;
}
public double getValue() {
    return this.value;
}

위의 예에서 게터를 동기화하는 데 어떤 점이 있습니까?


여기 에서 Java Concurrency in Practice 를 인용하는 것이 가장 좋습니다 .

공유 변수에 쓸 때만 동기화를 사용해야한다고 가정하는 것은 일반적인 실수입니다. 이것은 사실이 아닙니다.

둘 이상의 스레드에서 액세스 할 수있는 각 변경 가능한 상태 변수에 대해 해당 변수에 대한 모든 액세스 는 동일한 잠금을 보유한 상태에서 수행되어야합니다. 이 경우 변수가 해당 잠금 장치로 보호된다고 말합니다.

동기화가없는 경우 컴파일러, 프로세서 및 런타임은 작업이 실행되는 것처럼 보이는 순서대로 이상한 일을 할 수 있습니다. 불충분하게 동기화 된 다중 스레드 프로그램에서 메모리 동작이 "반드시"발생하는 순서를 추론하려는 시도는 거의 확실하지 않습니다.

일반적으로 프리미티브에 대해 그렇게주의 할 필요는 없습니다. 따라서 이것이 an int또는 a 이면 다음과 같을 boolean수 있습니다.

스레드가 동기화없이 변수를 읽을 때 오래된 값을 볼 수 있지만 적어도 임의의 값이 아닌 일부 스레드에 의해 실제로 배치 된 값을 볼 수 있습니다.

그러나 예를 들어 on long또는 double선언 되지 않은 경우와 같은 64 비트 작업 에는 해당되지 않습니다 volatile.

Java 메모리 모델에서는 페치 및 저장 작업이 원자 적이어야하지만 비 휘발성 long 및 double 변수의 경우 JVM은 64 비트 읽기 또는 쓰기를 두 개의 개별 32 비트 작업으로 처리 할 수 ​​있습니다. 읽기와 쓰기가 다른 스레드에서 발생하면 비 휘발성 long을 읽고 한 값의 상위 32 비트와 다른 값의 하위 32 비트를 되 찾을 수 있습니다.

따라서 오래된 값에 대해 신경 쓰지 않더라도 휘발성으로 선언되거나 잠금으로 보호되지 않는 한 다중 스레드 프로그램에서 공유 가변 long 및 double 변수를 사용하는 것은 안전하지 않습니다.


JIT가 코드를 컴파일하는 합법적 인 방법이 무엇인지 예를 들어 보여 드리겠습니다. 당신은 쓰기:

while (myBean.getValue() > 1.0) {
  // perform some action
  Thread.sleep(1);
}

JIT는 다음을 컴파일합니다.

if (myBean.getValue() > 1.0) 
  while (true) {
    // perform some action
    Thread.sleep(1);
  }

약간 다른 시나리오에서 Java 컴파일러조차도 유사한 바이트 코드를 자랑 할 수 있습니다 (단지 다른에 대한 동적 디스패치 가능성을 제거하면 됨 getValue). 게양 교과서의 예입니다.

이것이 합법적 인 이유는 무엇입니까? 컴파일러는 myBean.getValue()위의 코드를 실행하는 동안 의 결과가 변경 될 수 없다고 가정 할 권리가 있습니다. 없이는 synchronized다른 스레드의 작업을 무시할 수 있습니다.


그 이유는 스레드가 읽을 때 값을 업데이트하는 다른 스레드를 방지하여 오래된 값에 대한 작업을 수행하지 않기 위해서입니다.

여기서 get 메소드는 "this"에 대한 고유 잠금을 획득하므로 setter 메소드를 사용하여 설정 / 업데이트를 시도 할 수있는 다른 스레드는 get을 수행하는 스레드에 의해 이미 획득 된 setter 메소드에 들어가기 위해 "this"에 대한 잠금을 획득 할 때까지 기다려야합니다. .

이것이 변경 가능한 상태에서 작업을 수행 할 때 동일한 잠금을 사용하는 관행을 따르는 것이 권장되는 이유입니다.

복합 문이 없기 때문에 필드를 휘발성으로 만드는 것이 여기서 작동합니다.


동기화 된 메서드는 "this"인 고유 잠금을 사용한다는 점에 유의해야합니다. 따라서 get 및 set 모두 동기화된다는 것은 메서드에 들어가는 모든 스레드가 이에 대해 잠금을 획득해야 함을 의미합니다.


비 원자 64 비트 연산을 수행 할 때는 특별한 고려가 필요합니다. Java Concurrency In Practice에서 발췌 한 내용은 상황을 이해하는 데 도움이 될 수 있습니다.

"Java 메모리 모델은 가져 오기 및 저장 작업이 원자 적이어야하지만 비 휘발성 long 및 double 변수의 경우 JVM은 64 비트 읽기 또는 쓰기를 두 개의 개별 32 비트 작업으로 처리 할 수 ​​있습니다. 읽기 및 쓰기가 발생하면 따라서 비 휘발성 long을 읽고 한 값의 상위 32 비트와 다른 값의 하위 32 비트를 되돌릴 수 있습니다. 따라서 오래된 값에 대해 신경 쓰지 않더라도 안전하지 않습니다. 휘발성으로 선언되거나 잠금으로 보호되지 않는 한 다중 스레드 프로그램에서 공유 가변 long 및 double 변수를 사용하십시오. "


누군가에게는이 코드가 끔찍해 보이지만 잘 작동합니다.

  private Double value;
  public  void setValue(Double value){
    updateValue(value, true);
  }
  public Double getValue(){
      return updateValue(value, false);
  }
  private double updateValue(Double value,boolean set){
    synchronized(MyClass.class){
      if(set)
        this.value = value;
      return value;
    }
  }

참조 URL : https://stackoverflow.com/questions/11459543/should-getters-and-setters-be-synchronized

반응형