web-dev-qa-db-fra.com

Appel à la méthode de Java.text.DateFormat statique déconseillé?

Je reçois une erreur Find Bugs - lors de l’appel à la méthode statique Java.text.DateFormat et Je ne sais pas pourquoi il n’est pas bon/recommandé de faire les choses suivantes.

private static final Date TODAY = Calendar.getInstance().getTime();
private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); 

private String fileName = "file_" + yymmdd.format(TODAY);
40
setzamora

Les formats de date ne sont pas thread-safe, ce qui signifie qu'ils conservent une représentation interne de l'état. Leur utilisation dans un contexte statique peut générer des bugs assez étranges si plusieurs threads accèdent simultanément à la même instance.

Ma suggestion serait de rendre vos variables locales à l'endroit où vous les utilisez au lieu de les transformer en propriétés statiques de la classe. Il semble que vous puissiez le faire lorsque vous initialisez la classe. Vous pouvez donc le faire dans le constructeur:

public class MyClass {
    private String fileName;

    public MyClass() {
        final Date today = Calendar.getInstance().getTime();
        final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); 

        this.fileName = "file_" + yymmdd.format(TODAY);
    }
    ...
}

Et si vous devez utiliser le formateur à plusieurs endroits, vous pouvez simplement créer le modèle static final et créer une nouvelle variable locale DateFormat si nécessaire:

public class MyClass {
    private static final String FILENAME_DATE_PATTERN = "yyMMdd";

    public void myMethod() {
        final DateFormat format = new SimpleDateFormat(FILENAME_DATE_PATTERN);
        // do some formatting
    }
}

La documentation FindBugs pour le problème indique:

Comme le dit JavaDoc, DateFormats est intrinsèquement peu sûr pour multithread utilisation. Le détecteur a trouvé un appel à une instance de DateFormat qui a été obtenue via un champ statique. Ce semble suspect.

Pour plus d'informations à ce sujet, voir Sun Bug n ° 6231579 et Sun Bug n ° 6178997.

Et le javadoc pour DateFormat suggère:

Les formats de date ne sont pas synchronisés. Il est recommandé de créer séparé formater des instances pour chaque thread. Si plusieurs threads accèdent à un format simultanément, il doit être synchronisé à l'extérieur.

La réponse de Jack Leow a également un bon point à propos de la sémantique de votre utilisation statique de "TODAY".

Soit dit en passant, cela a effectivement été le cas dans un environnement de production à fort trafic, et le débogage est très déroutant au début; donc, dans mon expérience, l'avertissement FindBugs est en fait une suggestion utile (contrairement à certaines autres règles d'analyse statique, qui semblent parfois être tatillonnes).

76
Rob Hruska

Commons Lang a un FastDateFormat objet qui est thread-safe . Il ne fait que formater mais pas analyser.

Si vous pouvez utiliser commons-lang, cela pourrait bien fonctionner pour vous.

private static final Date TODAY = Calendar.getInstance().getTime();
private static final FastDateFormat yymmdd = FastDateFormat.getInstance("yyMMdd");

private String fileName = "file_" + yymmdd.format(TODAY);
14
ScArcher2

Je ne sais pas si FindBugs s'en plaint, mais un problème que je vois dans votre code est que vous définissez TODAY en tant que variable de niveau de classe (statique), constante (finale). Cela indique que vous souhaitez que TODAY ne change jamais (je ne crois pas que ce soit le cas, car Java.util.Dates peut être modifié, mais c'est une autre histoire).

Pensez à ce qui se passe si votre application s'exécute pendant plusieurs jours. TODAY (sauf si vous le mettez à jour) fera référence au jour où l'application a été démarrée, pas à la date actuelle. Êtes-vous sûr que c'est ce que vous vouliez dire?

Ce n'est peut-être pas du tout un bug dans votre code, mais l'intention n'est pas claire et je crois que peut être ce dont FindBugs se plaint.

4
Jack Leow

Êtes-vous sûr que ce n'est pas

private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); 

? C'est ce que le message d'erreur indique.

Je pense que son objectif est le fait que DateFormat n'est pas thread-safe, donc avoir une instance comme champ statique indique des conditions de concurrence potentielles.

4

Une alternative qui n’a pas été mentionnée est l’utilisation de ThreadLocal. Voir http://www.javacodegeeks.com/2010/07/Java-best-practices-dateformat-in.html pour plus d'informations + comparaison des performances entre les 3 options:

  • Créer une instance à chaque fois
  • Synchroniser l'accès
  • Utilisation de ThreadLocal

Exemple d'utilisation de ThreadLocal:

private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
    @Override
    protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyMMdd");
    }
};

Usage:

DATE_FORMAT.get().format( TODAY )
2
Wim Deblauwe

Ce n'est pas fil-safe, d'une part.

0
Pointy

Je suppose que c'est parce que le format n'est pas thread-safe?

(Je n'ai pas vu de quoi se plaindre findbugs, pouvez-vous fournir le texte d'avertissement ?)

0
Bjorn J

Vous pouvez obtenir cette solution en encapsulant toutes les références au DateFormat dans un bloc de synchronisation - assurez-vous simplement que tous les appels sont encapsulés dans l'objet même synchronize!

0
Paul Tomblin