Java Concurrent Hashmap initTable () 왜 try / finally 블록입니까?
나는 다음 코드를 보았습니다 ( 여기 에서 소스 ) '
/**
* Initializes table, using the size recorded in sizeCtl.
*/
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
누군가 try 블록이 필요한 이유를 설명 할 수 있습니까?
답변
확인되지 않은 예외 및 오류는 개념으로 존재합니다. .put()ConcurrentHashMap 을 호출 하고 메모리가 부족하면 해당 맵이 배열을 만들려고 할 수 있으며 해당 호출이 OutOfMemoryError로 실패 할 수 있습니다. 코드는 여전히 계속되고 (아마도 이것을 포착하는 catch 블록에서) 해당 맵에 대한 참조가 여전히 존재합니다. 그 후 sizeCtl에 깨진 값이 있기 때문에 맵이 충돌하고 완전히 무효화되면 약간의 문제가 될 수 있습니다.
요점은 sizeCtl값 을 복원하는 finally 블록 입니다. 이것은 액세스 권한을 얻는 스레드 관리를 포함하여 주목할만한 다양한 작업에 사용됩니다. 그렇지 않은 경우 다른 풋 콜은 영원히 회전합니다.
이것의 관련성 (예 : 여기에서 throwable이 발생하는 빈도와 'try'및 'finally'를 제거하고 sizeCtl = sc;try / finally의 추가 오버 헤드없이 끝나는 경우)는 낮지 만 관련성이있는 경우 상당히 관련이 있습니다.
이것은 일종의 뮤텍스이며 이중 확인 잠금입니다.
첫째로
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
sizeCtl뮤텍스를 확인합니다 . 0보다 작 으면 다른 사람이 잡은 것이므로 잠시 기다립니다.
0 (또는 그 이상)이면 잠금을 얻을 가능성이 있습니다. 따라서 Compare-And-Swap이 수행됩니다.
if (U.compareAndSetInt(this, SIZECTL, sc, -1)
이것은 원자 적 작업입니다. 다른 스레드가 첫 번째 검사와 두 번째 검사 사이에 그것을 잡았다면은 if취해지지 않을 것입니다.
CAS가 성공하면 메서드가 뮤텍스를 해제 할 책임이 있습니다. 따라서 a try-finally는 sizeCtl예외가 발생하는지 (예 : 새 노드를 할당하는 메모리 부족-또는 맵에 적용 할 수있는 고유 한 제한이있는 경우) 여부에 관계없이 뮤텍스가 해제 ( 원래 값으로 재설정) 되도록하는 데 사용됩니다 .