BufferedInputStream, alanı doğrudan kullanmak yerine neden bir alanı yerel bir değişkene kopyalıyor?


107

Kaynak kodunu okuduğumda, java.io.BufferedInputStream.getInIfOpen()neden böyle bir kod yazdığına dair kafam karıştı:

/**
 * 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;
}

Alan değişkenini indoğrudan aşağıdaki gibi kullanmak yerine neden takma adı kullanıyor?

/**
 * 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;
}

Birisi makul bir açıklama yapabilir mi?


İçinde Eclipse, bir ififadede hata ayıklayıcıyı duraklatamazsınız . Olabilir o takma değişken için bir neden. Sadece oraya atmak istedim. Tabii ki spekülasyon yapıyorum.
Debosmit Ray

@DebosmitRay: Gerçekten ififadeyi duraklatamıyor musunuz?
rkosegi

Eclipse benim sürümü üzerinde @rkosegi, sorun benzer bu bir . Çok yaygın bir olay olmayabilir. Her neyse, bunu hafif bir notla kastetmedim (açıkça kötü bir şaka). :)
Debosmit Ray

Yanıtlar:


119

Bu koda bağlam dışında bakarsanız, bu "takma ad" için iyi bir açıklama yoktur. Bu sadece gereksiz kod veya zayıf kod stilidir.

Ancak bağlam, BufferedInputStreamalt sınıflara ayrılabilen bir sınıf ve çok iş parçacıklı bir bağlamda çalışması gerektiğidir.

İpucu olduğunu inbeyan edilir FilterInputStreamDİR protected volatile. Bir alt sınıf ve atama ulaşabileceğini bir şans olduğunu vasıtası That nulliçin in. Bu olasılık göz önüne alındığında, "takma ad" aslında bir yarış durumunu önlemek için oradadır.

Kodu "takma ad" olmadan düşünün

private InputStream getInIfOpen() throws IOException {
    if (in == null)
        throw new IOException("Stream closed");
    return in;
}
  1. Konu A aramaları getInIfOpen()
  2. A iş parçacığı , olmadığını değerlendirir in == nullve görür .innull
  3. B atar Konu nulliçin in.
  4. A iş parçacığı yürütülür return in. Hangi döndürür nullçünkü abir volatile.

"Takma ad" bunu engeller. Şimdi inA evresi tarafından sadece bir kez okunur. Eğer B evresi nullA evresine sahip olduktan sonra atarsa inbunun önemi yoktur. A iş parçacığı ya bir istisna atar ya da (garantili) boş olmayan bir değer döndürür.


11
Bu, protecteddeğişkenlerin neden çok iş parçacıklı bir bağlamda kötü olduğunu gösterir .
Mick Mnemonic

2
Gerçekten öyle. Ancak, AFAIK bu sınıflar Java 1.0'a kadar geri gider. Bu, müşteri kodunu kırma korkusuyla düzeltilemeyecek kötü bir tasarım kararının başka bir örneğidir.
Stephen C

2
@StephenC Ayrıntılı açıklama +1 için teşekkürler. Bu protected, çok iş parçacıklıysa kodumuzda değişkenler kullanmamamız gerektiği anlamına mı geliyor?
Madhusudana Reddy Sunnapu

3
@MadhusudanaReddySunnapu Genel ders, birden fazla iş parçacığının aynı duruma erişebildiği bir ortamda, bu erişimi bir şekilde kontrol etmeniz gerektiğidir. Bu, yalnızca ayarlayıcı aracılığıyla erişilebilen özel bir değişken olabilir, bunun gibi yerel bir koruma olabilir, değişkeni bir kez yazılabilir bir şekilde evreli bir şekilde yaparak olabilir.
Chris Hayes

3
@sam - 1) Tüm yarış koşullarını ve durumlarını açıklamaya gerek yok. Cevabın amacı, görünüşte açıklanamayan bu kodun gerçekte neden gerekli olduğuna işaret etmektir. 2) Nasıl yani?
Stephen C

20

Bunun nedeni, sınıfın BufferedInputStreamçok iş parçacıklı kullanım için tasarlanmış olmasıdır.

Burada, inüst sınıfa yerleştirilen bildirimini görüyorsunuz FilterInputStream:

protected volatile InputStream in;

Olduğundan protected, değeri ve alt sınıfları FilterInputStreamdahil olmak üzere herhangi bir alt sınıfı tarafından değiştirilebilir BufferedInputStream. Ayrıca, bildirilmiştir volatile, yani herhangi bir evre değişkenin değerini değiştirirse, bu değişiklik hemen diğer tüm evrelerde yansıtılacaktır. Bu kombinasyon kötüdür, çünkü sınıfın BufferedInputStreamne zaman indeğiştirileceğini kontrol etmenin veya bilmenin bir yolu olmadığı anlamına gelir . Bu nedenle, değer, null BufferedInputStream::getInIfOpeniçin kontrol ile null kontrolünü etkin bir şekilde işe yaramaz hale getiren geri dönüş ifadesi arasında bile değiştirilebilir . inYerel değişkende önbelleğe almak için değerini yalnızca bir kez okuyarak, yerel değişkenler her zaman tek bir iş parçacığına ait olduğundan input, yöntem BufferedInputStream::getInIfOpendiğer evrelerden gelen değişikliklere karşı güvenlidir.

Null olarak BufferedInputStream::closeayarlanan bir örnek var 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()
    }
}

Yürütüldüğünde BufferedInputStream::closebaşka bir iş parçacığı tarafından çağrılırsa BufferedInputStream::getInIfOpen, bu yukarıda açıklanan yarış koşuluyla sonuçlanır.


Biz gibi şeyler görüyoruz mademki Kabul ediyorum, compareAndSet(), CASkod ve yorumlarda, vb. Ayrıca BufferedInputStreamkodu araştırdım ve çok sayıda synchronizedyöntem buldum . Bu yüzden, çok iş parçacıklı kullanım için tasarlandı, ancak kesinlikle bu şekilde kullanmadım. Her neyse, cevabınızın doğru olduğunu düşünüyorum!
sparc_spread

Bu muhtemelen mantıklıdır çünkü getInIfOpen()yalnızca public synchronizedyöntemlerinden çağrılır BufferedInputStream.
Mick Mnemonic

6

Bu çok kısa bir koddur, ancak teorik olarak, çok iş parçacıklı bir ortamda, inkarşılaştırmadan hemen sonra değişebilir, böylece yöntem kontrol etmediği bir şeyi döndürebilir (geri dönebilir null, böylece tam olarak yapması gereken şeyi yapabilir. önlemek).


Ben referans söylersek ben düzeltmek muyum in olabilir Eğer (çok dişli bir ortamda) değerinin yöntem ve dönüş çağrısı zaman arasında değiştirmek?
Debosmit Ray

Evet, bunu söyleyebilirsin. Nihayetinde olasılık gerçekten somut duruma bağlı olacaktır (bir gerçek için bildiğimiz tek şey, bunun inher an değişebileceğidir).
acdcjunior

4

Sınıf değişkenini inyerel değişkene yakalamanın , çalışırken başka bir iş parçacığı tarafından değiştirilirse inputtutarsız davranışı önlemek olduğuna inanıyorum .ingetInIfOpen()

Sahibinin inüst sınıf olduğuna ve bunu olarak işaretlemediğine dikkat edin final.

Bu model, sınıfın diğer bölümlerinde de tekrarlanır ve makul bir savunma kodlaması gibi görünmektedir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.