[java] BufferedInputStream이 필드를 직접 사용하지 않고 지역 변수에 필드를 복사하는 이유

에서 소스 코드를 읽을 때 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;
}
  1. 스레드 A 호출 getInIfOpen()
  2. 스레드 A in == null는 그것이 in아닌 것을 평가 하고 봅니다 null.
  3. 스레드 B nullin.
  4. 스레드 A는 return in. 어느 반환 null하기 때문에 aA는 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경우 일관성없는 동작을 방지 하는 것이라고 생각 합니다.ingetInIfOpen()

의 소유자 in는 상위 클래스이며으로 표시하지 않습니다 final.

이 패턴은 클래스의 다른 부분에서 복제되며 합리적인 방어 코딩으로 보입니다.


답변