2016-03-26 11 views
107

、私はそれがこのようなコードを書いた理由について混乱しています代わりに、直接以下のようにフィールドに変数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; 
} 

誰かが合理的な説明を与えることができますか?

+0

'Eclipse'では、デバッガを' if'文で一時停止することはできません。 *エイリアス変数の理由かもしれません。ただそれをそこに投げたかった。私はもちろん、推測します。 –

+0

@DebosmitRay:本当に 'if'文で一時停止できませんか? – rkosegi

+0

@rkosegi Eclipseの私のバージョンでは、この問題は[this one](http://stackoverflow.com/questions/20413287/bug-with-eclipse-debuggers-stepover)に似ています。非常に一般的な出来事ではないかもしれません。そして、とにかく、私は軽いメモ(明らかに悪い冗談)でそれを意味しませんでした。 :) –

答えて

119

このコードを文脈から見れば、その「エイリアス」についての説明はありません。それは単に冗長なコードまたは貧弱なコードスタイルです。

しかし、文脈では、BufferedInputStreamはサブクラス化できるクラスであり、マルチスレッドコンテキストで動作する必要があります。

FilterInputStreaminが宣言されているというのは、protected volatileです。つまり、サブクラスが到達し、nullinに割り当てる可能性があります。その可能性を考えると、「エイリアス」は実際に競合状態を防ぐために存在します。

private InputStream getInIfOpen() throws IOException { 
    if (in == null) 
     throw new IOException("Stream closed"); 
    return in; 
} 
  1. スレッドAが呼び出す

    は "エイリアス" せずにコードを考えてみましょうgetInIfOpen()

  2. スレッドAはin == nullを評価し、innullではないことを認識します。
  3. スレッドBはnullinを割り当てます。
  4. スレッドAはreturn inを実行します。 avolatileなのでnullを返します。

"エイリアス"がこれを防止します。今度はinがスレッドAによって一度だけ読み込まれます。スレッドAがinの後にスレッドBがnullを割り当てた場合は問題ありません。スレッドAは例外をスローするか、(保証された)非ヌル値を返します。

+11

なぜ 'protected'変数がマルチスレッドコンテキストで悪いのかを示しています。 –

+2

それは確かにあります。しかし、AFAIKはこれらのクラスをJava 1.0に戻しています。これは、顧客コードを壊す恐れがあるために修正できなかった設計上の意思決定のもう1つの例です。 –

+2

@StephenC詳細な説明をありがとう+1。つまり、マルチスレッドの場合、コード内で 'protected'変数を使用すべきではありませんか? –

20

これは、クラスBufferedInputStreamがマルチスレッド使用のために設計されているためです。

protected volatile InputStream in; 

それはprotectedあるので、その値はBufferedInputStreamおよびそのサブクラスを含め、FilterInputStreamの任意のサブクラスによって変更することができます:ここで

は、あなたが親クラスFilterInputStreamに置かれinの宣言を参照してください。また、volatileと宣言されています。つまり、いずれかのスレッドが変数の値を変更すると、この変更は他のすべてのスレッドに即座に反映されます。この組み合わせは悪いです。BufferedInputStreamは、inが変更されたときを制御または知る手段がないことを意味するためです。したがって、nullのチェックとreturn文の間で値をBufferedInputStream::getInIfOpenに変更することもできます。これにより、nullが無駄になることを効果的にチェックします。inの値を1回だけ読み取ってローカル変数inputにキャッシュすると、BufferedInputStream::getInIfOpenメソッドはローカル変数が常に1つのスレッドによって所有されるため、他のスレッドからの変更に対して安全です。

例は、nullにinを設定する、BufferedInputStream::closeであり:

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::getInIfOpen実行中BufferedInputStream::closeが別のスレッドによって呼び出された場合、これは上記の競合状態をもたらすであろう。

+0

コードやコメントに 'compareAndSet()'、 'CAS'などの情報があるので、私は同意します。私は 'BufferedInputStream'コードを検索し、多数の' synchronized'メソッドを発見しました。だから、それはマルチスレッドの使用を意図していますが、私はそれを一度も使ったことはありません。とにかく、あなたの答えは正しいと思います! –

+0

これは、 'getInIfOpen()'が 'BufferedInputStream'の' public synchronized'メソッドからのみ呼び出されるため、おそらく意味があります。 –

6

これは、このような短いコードですが、理論的に、マルチスレッド環境で、inは、右の比較後に変更されることがあり、その方法は、それはこのようにやって、それがnullを返すことができます(チェックしていなかった何かを返すことができますそれが防ぐことを意図した正確なもの)。

+0

メソッドを呼び出すときと値を返すとき(マルチスレッド環境のとき)に、 'in' *という参照が変更されるかもしれないと言ったら正解でしょうか? –

+0

はい、あなたはそれを言うことができます。最終的には、具体的なケースに実際に依存する可能性があります(私たちが知っている事実は、いつでも変更できるということです)。 – acdcjunior

4

私はgetInIfOpen()の実行中にinが別のスレッドによって変更されている場合は、ローカル変数inputからin変数クラスをキャプチャすることは一貫性のない動作を防ぐためであると考えています。

inの所有者が親クラスであり、finalとマークしていないことに注意してください。

このパターンは、クラスの他の部分で複製され、合理的な防御コーディングと思われます。