Appel à la méthode de statique java.text.DateFormat pas conseillé?

Je reçois une erreur Find Bugsappel à la méthode static java.text.DateFormat et je ne connais pas la raison pour laquelle il n’est pas bon / conseillé de faire ces choses ci-dessous.

private static final Date TODAY = Calendar.getInstance().getTime(); private static final DateFormat yymmdd = new SimpleDateFormat("yyMMdd"); private Ssortingng fileName = "file_" + yymmdd.format(TODAY); 

DateFormats ne sont pas thread-safe, ce qui signifie qu’ils maintiennent une représentation interne de l’état. Leur utilisation dans un contexte statique peut générer des bogues 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 rendre statiques de la classe. Il semble que vous fassiez cela lorsque vous initialisez la classe, vous pouvez donc le faire dans le constructeur:

 public class MyClass { private Ssortingng 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 rendre le modèle static final et créer un nouveau DateFormat date local si nécessaire:

 public class MyClass { private static final Ssortingng FILENAME_DATE_PATTERN = "yyMMdd"; public void myMethod() { final DateFormat format = new SimpleDateFormat(FILENAME_DATE_PATTERN); // do some formatting } } 

La documentation FindBugs du problème indique:

Comme l’indique JavaDoc, les DateFormats sont insortingnsèquement dangereux pour une utilisation multithread. Le détecteur a trouvé un appel à une instance de DateFormat obtenue via un champ statique. Cela semble suspect.

Pour plus d’informations à ce sujet, consultez le bogue n ° 6231579 et le bogue n ° 6178997 de Sun.

Et le javadoc de DateFormat suggère:

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

La réponse de Jack Leow a également un bon argument sur la sémantique de votre utilisation statique de “TODAY”.

En passant, j’ai effectivement vu cela se produire dans un environnement de production à fort trafic, et c’est une chose très déroutante à déboguer au début; donc, dans mon expérience, l’avertissement FindBugs est en fait une suggestion utile (contrairement à d’autres règles d’parsing statique, qui semblent parfois être épineuses).

Commons Lang a un object FastDateFormat qui est thread-safe. Il ne fait que le formatage, pas l’parsing.

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 Ssortingng fileName = "file_" + yymmdd.format(TODAY); 

Ê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 le but est que DateFormat ne soit pas thread-safe, donc avoir une instance en tant que champ statique indique des conditions de course potentielles.

Je ne sais pas si FindBugs se plaint de cela, mais un problème que je vois avec votre code est que vous définissez TODAY comme une variable de classe (statique), constante (finale). Cela communique l’intention que vous souhaitez TODAY pour ne jamais changer (je ne crois pas que ce soit le cas, car java.util.Dates sont mutables, mais c’est une autre histoire).

Pensez à ce qui se passe si votre application fonctionne 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 du jour. Êtes-vous sûr que c’est ce que vous vouliez dire?

Ce n’est peut-être pas un bogue dans votre code, mais l’intention n’est pas claire, et je pense que c’est peut- être ce dont FindBugs se plaint.

Une alternative non mentionnée utilise 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
  • Accès synchronisé
  • Utiliser ThreadLocal

Exemple d’utilisation de ThreadLocal:

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

Usage:

 DATE_FORMAT.get().format( TODAY ) 

Ce n’est pas sûr, pour une chose.

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

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

Vous pouvez faire en sorte que cela disparaisse en encapsulant toutes les références à la DateFormat dans un bloc de synchronisation – assurez-vous simplement que tous les appels sont regroupés dans le même object de synchronisation!