106 votes

Pourquoi ne BufferedInputStream copier un champ dans une variable locale plutôt qu’utiliser le champ directement

Quand j’ai lu le code source de `` , je suis confus au sujet de pourquoi il a écrit de code comme celui-ci :

Pourquoi il utilise l’alias au lieu d’utiliser la variable de champ `` directement comme ci-dessous :

Est-ce que quelqu'un peut donner une explication raisonnable ?

117voto

Stephen C Points 255558

Si vous regardez ce code hors contexte, il n'y a pas de bonne explication pour le fait que "alias". Il est tout simplement redondants code ou de mauvais style de code.

Mais le contexte est qu' BufferedInputStream est une classe qui peut être sous-classé, et qu'il a besoin de travailler dans un environnement multi-thread contexte.

L'idée est qu' in est déclaré en FilterInputStream est protected volatile. Cela signifie qu'il ya une chance qu'une sous-classe pourrait atteindre et affecter null de in. Compte tenu de cette possibilité, le "alias" est en fait là pour éviter qu'une condition de course.

Considérez le code sans le "alias"

private InputStream getInIfOpen() throws IOException {
    if (in == null)
        throw new IOException("Stream closed");
    return in;
}
  1. Enfiler les appels getInIfOpen()
  2. Enfiler Une évalue in == null et voit qu' in n'est null.
  3. Thread B cède null de in.
  4. Thread exécute return in. Qui renvoie null car a est volatile.

Le "alias" permet d'éviter cela. Maintenant, in est lu qu'une fois par filetage A. Si thread B cède null après que le thread a, in il n'a pas d'importance. Thread va jeter une exception ou un retour (garanti) valeur non null.

20voto

Stefan Dollase Points 1781

C'est parce que la classe BufferedInputStream est conçu pour le multi-thread d'utilisation.

Ici, vous voyez la déclaration de in, qui est placé dans la classe parente FilterInputStream:

protected volatile InputStream in;

Depuis qu'il est protected, sa valeur peut être modifiée par aucune sous-classe d' FilterInputStream, y compris l' BufferedInputStream et de ses sous-classes. Aussi, il est déclaré volatile, ce qui signifie que si un thread modifie la valeur de la variable, cette modification est immédiatement prise en compte dans tous les autres threads. Cette combinaison est mauvais, car il s'agit de la classe BufferedInputStream n'a aucun moyen de contrôle ou de savoir quand in est changé. Ainsi, la valeur peut encore être changé entre le vérifier la nullité et de l'instruction de retour en BufferedInputStream::getInIfOpen, ce qui rend effectivement le vérifier la valeur null inutile. Par la lecture de la valeur de in qu'une seule fois pour mettre en cache dans la variable locale input, la méthode de BufferedInputStream::getInIfOpen est protégé contre les modifications d'autres threads, puisque les variables locales sont toujours détenue par un seul thread.

Il y a un exemple dans BufferedInputStream::close, ce qui définit in null:

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()
    }
}

Si BufferedInputStream::close est appelé par un autre thread, tout BufferedInputStream::getInIfOpen est exécutée, cela aurait pour résultat la race des conditions décrites ci-dessus.

6voto

acdcjunior Points 19898

Il s’agit d’un tel code court, mais, en théorie, dans un environnement multithread, peut modifier le droit après la comparaison, afin que la méthode peut retourner quelque chose il ne vérifie pas (elle pourrait retourner , faisant ainsi exactement la chose qu’il était censé prévenir).

4voto

sam Points 1738

Selon moi, capturant la variable de classe à la variable locale est d’empêcher un comportement incohérent si est changé par un autre thread alors que est en cours d’exécution.

Notez que le propriétaire de est la classe parente et ne marque pas comme .

Ce modèle est reproduit dans d’autres parties de la classe et semble être raisonnable de codage défensives.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X