나는 John의 의견 중 하나에 동의합니다 . 변수의 참조가 변경되는 경우 불일치를 방지하기 위해 비 최종 변수에 액세스하는 동안 항상 최종 잠금 더미를 사용해야합니다. 따라서 어떤 경우에도 첫 번째 경험 법칙 :
규칙 # 1 : 필드가 최종이 아닌 경우 항상 (개인) 최종 잠금 더미를 사용하십시오.
이유 # 1 : 잠금을 유지하고 직접 변수의 참조를 변경합니다. 동기화 된 잠금 외부에서 대기중인 다른 스레드는 보호 된 블록에 들어갈 수 있습니다.
이유 # 2 : 잠금을 유지하고 다른 스레드가 변수의 참조를 변경합니다. 결과는 동일합니다. 다른 스레드가 보호 된 블록에 들어갈 수 있습니다.
그러나 최종 잠금 더미를 사용할 때 또 다른 문제 가 있습니다. 동기화 (object)를 호출 할 때 최종이 아닌 개체는 RAM 과만 동기화되기 때문에 잘못된 데이터를 얻을 수 있습니다. 따라서 두 번째 경험 법칙으로 :
규칙 # 2 : 최종 잠금이 아닌 개체를 잠글 때 항상 두 가지를 모두 수행해야합니다. RAM 동기화를 위해 최종 잠금 더미와 최종 잠금이 아닌 개체의 잠금을 사용합니다. (유일한 대안은 객체의 모든 필드를 휘발성으로 선언하는 것입니다!)
이러한 잠금을 "중첩 잠금"이라고도합니다. 항상 동일한 순서로 호출해야합니다. 그렇지 않으면 데드락이 발생합니다 .
public class X {
private final LOCK;
private Object o;
public void setO(Object o){
this.o = o;
}
public void x() {
synchronized (LOCK) {
synchronized(o){
}
}
}
}
보시다시피 두 자물쇠는 항상 함께 속하기 때문에 동일한 줄에 직접 작성합니다. 이와 같이 10 개의 중첩 잠금을 수행 할 수도 있습니다.
synchronized (LOCK1) {
synchronized (LOCK2) {
synchronized (LOCK3) {
synchronized (LOCK4) {
}
}
}
}
이 코드는 synchronized (LOCK3)
다른 스레드에서 와 같이 내부 잠금을 획득하는 경우 중단되지 않습니다 . 그러나 다음과 같은 다른 스레드를 호출하면 중단됩니다.
synchronized (LOCK4) {
synchronized (LOCK1) {
synchronized (LOCK3) {
synchronized (LOCK2) {
}
}
}
}
최종이 아닌 필드를 처리하는 동안 이러한 중첩 된 잠금에 대한 해결 방법은 하나뿐입니다.
규칙 # 2-대안 : 개체의 모든 필드를 휘발성으로 선언합니다. (여기서는이 작업의 단점에 대해 설명하지 않을 것입니다. 예를 들어 읽기를위한 x 레벨 캐시의 스토리지 방지, aso.)
따라서 aioobe가 옳습니다. java.util.concurrent를 사용하십시오. 또는 동기화에 대한 모든 것을 이해하고 중첩 된 잠금을 사용하여 직접 수행하십시오. ;)
최종 필드가 아닌 필드의 동기화가 중단되는 이유에 대한 자세한 내용은 내 테스트 사례를 살펴보십시오. https://stackoverflow.com/a/21460055/2012947
RAM 및 캐시로 인해 동기화가 필요한 이유에 대한 자세한 내용은 https://stackoverflow.com/a/21409975/2012947을 참조 하십시오.
o
동기화 된 블록에 도달 한 시간에 언급했다.o
참조 하는 객체 가 변경되면 다른 스레드가 따라와 동기화 된 코드 블록을 실행할 수 있습니다.