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


107

에서 소스 코드를 읽을 때 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;
}

누군가 합리적인 설명을 할 수 있습니까?


에서는 문 Eclipse에서 디버거를 일시 중지 할 수 없습니다 if. 그 별칭 변수에 대한 이유. 그냥 그걸 버리고 싶었어요. 물론 추측합니다.
Debosmit Ray

@DebosmitRay : 정말로 if성명서에서 멈출 수 없습니까?
rkosegi

이클립스의 내 버전에 @rkosegi는, 문제는 유사하다 이 하나 . 매우 흔한 일이 아닐 수도 있습니다. 그리고 어쨌든, 나는 가벼운 노트에서 그것을 의미하지 않았습니다 (분명히 나쁜 농담). :)
Debosmit Ray

답변:


119

이 코드를 문맥 밖에서 보면 그 "별칭"에 대한 좋은 설명이 없습니다. 단순히 중복 코드이거나 잘못된 코드 스타일입니다.

그러나 컨텍스트는 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는 예외를 발생 시키거나 널이 아닌 값을 (보장 된) 반환합니다.


11
protected다중 스레드 컨텍스트에서 변수가 왜 나쁜지 보여줍니다 .
믹 니모닉

2
실제로 그렇습니다. 그러나 AFAIK 이러한 클래스는 Java 1.0으로 돌아갑니다. 이것은 고객 코드를 깨는 것을 두려워하여 수정할 수없는 잘못된 설계 결정의 또 다른 예일뿐입니다.
스티븐 C

2
@StephenC 자세한 설명 감사합니다 +1. 그렇다면 protected다중 스레드 인 경우 코드에서 변수를 사용하지 않아야한다는 의미 입니까?
Madhusudana 레디 Sunnapu

3
@MadhusudanaReddySunnapu 전반적인 교훈은 여러 스레드가 동일한 상태에 액세스 할 수있는 환경에서 어떻게 든 해당 액세스를 제어해야한다는 것입니다. 그것은 setter를 통해서만 접근 할 수있는 private 변수 일 수 있고, 이와 같은 로컬 가드가 될 수 있습니다. 그것은 쓰레드 세이프 방식으로 변수를 한 번만 쓸 수 있도록함으로써 가능합니다.
크리스 헤이즈

3
@sam-1) 모든 경쟁 조건과 상태를 설명 할 필요는 없습니다. 대답의 목적은 설명 할 수없는이 코드가 실제로 필요한 이유를 지적하는 것 입니다. 2) 어떻게?
Stephen C

20

이는 클래스 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위에서 설명한 경쟁 조건이 발생합니다.


우리는 같은 것을보고있는 같은 진대 내가 동의 compareAndSet(), CAS코드에서와 의견 등. 나는 또한 BufferedInputStream코드를 검색하고 수많은 synchronized방법을 발견했습니다 . 그래서, 그것은 멀티 스레드 사용을위한 것입니다. 물론 그런 식으로 사용한 적이 없습니다. 어쨌든, 나는 당신의 대답이 맞다고 생각합니다!
sparc_spread

의 메서드 getInIfOpen()에서만 호출 되기 때문에 이것은 아마도 의미 public synchronizedBufferedInputStream있습니다.
Mick Mnemonic

6

이것은 매우 짧은 코드이지만 이론적으로 다중 스레드 환경에서는 in비교 직후에 변경 될 수 있으므로 메서드가 확인하지 않은 것을 반환 할 수 있습니다 (를 반환 할 수 null있으므로 의도 한대로 정확하게 수행합니다. 막다).


메서드를 호출하고 값을 반환하는 시간 (다중 스레드 환경에서) 사이에 참조 in 변경 될 수 있다고 말하는 것이 맞 습니까?
Debosmit Ray

예, 그렇게 말할 수 있습니다. 궁극적으로 가능성은 구체적인 사례에 따라 달라집니다 (사실에 대해 아는 것은 in언제든지 변경 될 수 있다는 것입니다 ).
acdcjunior

4

클래스 변수 in를 로컬 변수 로 캡처 하는 것은 실행 중에 다른 스레드에 의해 변경되는 input경우 일관성없는 동작을 방지 하는 것이라고 생각 합니다.ingetInIfOpen()

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

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.