에서 소스 코드를 읽을 때 java.io.BufferedInputStream.getInIfOpen()
다음과 같은 코드를 작성한 이유가 혼란 스럽습니다.
/**
* Check to make sure that underlying input stream has not been
* nulled out due to close; if not return it;
*/
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
in
아래와 같이 필드 변수를 직접 사용하는 대신 별칭을 사용하는 이유는 무엇입니까?
/**
* Check to make sure that underlying input stream has not been
* nulled out due to close; if not return it;
*/
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
누군가 합리적인 설명을 할 수 있습니까?
답변
이 코드를 문맥 밖에서 보면 그 “별칭”에 대한 좋은 설명이 없습니다. 단순히 중복 코드이거나 잘못된 코드 스타일입니다.
그러나 컨텍스트는 BufferedInputStream
서브 클래 싱 할 수있는 클래스이고 다중 스레드 컨텍스트에서 작동해야한다는 것입니다.
단서는에서 in
선언 된 것 FilterInputStream
입니다 protected volatile
. 서브 클래스 및 할당에 도달 할 수있는 기회가 있다는 것을 의미 null
에가 in
. 그 가능성을 감안할 때 “별칭”은 실제로 경쟁 조건을 방지하기 위해 존재합니다.
“별칭”이없는 코드를 고려하십시오.
private InputStream getInIfOpen() throws IOException {
if (in == null)
throw new IOException("Stream closed");
return in;
}
- 스레드 A 호출
getInIfOpen()
- 스레드 A
in == null
는 그것이in
아닌 것을 평가 하고 봅니다null
. - 스레드 B
null
는in
. - 스레드 A는
return in
. 어느 반환null
하기 때문에a
A는volatile
.
“별칭”은이를 방지합니다. 이제 in
스레드 A가 한 번만 읽습니다. 스레드 A가 스레드 B를 지정한 null
후에 스레드 B가 할당하면 in
문제가되지 않습니다. 스레드 A는 예외를 발생 시키거나 널이 아닌 값을 (보장 된) 반환합니다.
답변
이는 클래스 BufferedInputStream
가 다중 스레드 사용을 위해 설계 되었기 때문 입니다.
여기 in
에서 부모 클래스에있는 의 선언을 볼 수 있습니다 FilterInputStream
.
protected volatile InputStream in;
이므로 protected
해당 값은 및 하위 클래스를 FilterInputStream
포함하여 의 모든 하위 클래스에 의해 변경 될 수 있습니다 BufferedInputStream
. 또한 선언 volatile
되어 있습니다. 즉, 스레드가 변수 값을 변경하면이 변경 사항이 다른 모든 스레드에 즉시 반영됩니다. 이 조합은 클래스 가 변경 BufferedInputStream
시기를 제어하거나 알 수있는 방법이 없음을 의미하기 때문에 좋지 않습니다 in
. 따라서 null 검사와의 return 문간에 값을 변경할 수도 있으므로 BufferedInputStream::getInIfOpen
효과적으로 null 검사를 쓸모 없게 만듭니다. 값을 in
한 번만 읽어 로컬 변수에 캐시하면 로컬 변수가 항상 단일 스레드에 의해 소유되기 때문에 input
메서드 BufferedInputStream::getInIfOpen
는 다른 스레드의 변경에 대해 안전합니다.
에 null로 BufferedInputStream::close
설정된 예제가 있습니다 in
.
public void close() throws IOException {
byte[] buffer;
while ( (buffer = buf) != null) {
if (bufUpdater.compareAndSet(this, buffer, null)) {
InputStream input = in;
in = null;
if (input != null)
input.close();
return;
}
// Else retry in case a new buf was CASed in fill()
}
}
이 실행되는 BufferedInputStream::close
동안 다른 스레드에 의해 호출 되면 BufferedInputStream::getInIfOpen
위에서 설명한 경쟁 조건이 발생합니다.
답변
이것은 매우 짧은 코드이지만 이론적으로 다중 스레드 환경에서는 in
비교 직후에 변경 될 수 있으므로 메서드가 확인하지 않은 것을 반환 할 수 있습니다 (를 반환 할 수 null
있으므로 의도 한대로 정확하게 수행합니다. 막다).
답변
클래스 변수 in
를 로컬 변수 로 캡처 하는 것은 실행 중에 다른 스레드에 의해 변경되는 input
경우 일관성없는 동작을 방지 하는 것이라고 생각 합니다.in
getInIfOpen()
의 소유자 in
는 상위 클래스이며으로 표시하지 않습니다 final
.
이 패턴은 클래스의 다른 부분에서 복제되며 합리적인 방어 코딩으로 보입니다.