게터와 세터를 동기화해야합니까?
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
'Development Tip' 카테고리의 다른 글
'open'제네릭 형식과 함께 IsAssignableFrom 사용 (0) | 2020.12.30 |
---|---|
Hibernate 오류 :이 클래스의 ID는 save ()를 호출하기 전에 수동으로 할당되어야합니다. (0) | 2020.12.30 |
XML을 동적 C # 객체로 변환 (0) | 2020.12.30 |
Python으로 YAML 파일을 읽으면 yaml.composer.ComposerError : 스트림에 단일 문서가 필요합니다. (0) | 2020.12.30 |
누름 (0) | 2020.12.30 |