2010-03-09 20 views
37

estoy recibiendo un encontrar errores error - llamada al método de java.text.DateFormat estática y No sé la razón por la que no es bueno/aconsejable estar haciendo estas cosas a continuación.Llamada al método de estática java.text.DateFormat no aconsejable?

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

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

Aparte del método de llamada, este código se ve un poco sospechoso - 'TODAY' será un día constante, ¿el formateador es un final no estático? – Pool

+2

También HOY no siempre puede ser "hoy", si, por ejemplo, se carga esta clase y luego se deja ejecutar la JVM hasta el día siguiente; cualquier lógica que dependa de HOY para ser el día actual no funcionará, a menos que esté contabilizando esta discrepancia – Peter

+0

@ Peter: Sí, el programa se restablece para cada ejecución. – Joset

Respuesta

68

Los DateFormats no son seguros para subprocesos, lo que significa que mantienen una representación interna del estado. Usarlos en un contexto estático puede producir algunos errores bastante extraños si varios hilos acceden a la misma instancia simultáneamente.

Mi sugerencia sería hacer que sus variables locales a donde las está utilizando en lugar de convertirlas en propiedades estáticas de la clase. Parece que podría estar haciendo esto cuando se está inicializando la clase, por lo que podría hacer esto en el constructor:

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); 
    } 
    ... 
} 

Y si es necesario utilizar el formateador en múltiples lugares, que sólo podría hacer que el patrón static final y crear un nuevo DateFormat local cuando sea necesario:

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

el FindBugs documentation para el tema dice:

Como los estados JavaDoc, DateFormats son intrínsecamente inseguro para uso multiproceso . El detector ha encontrado una llamada al se ha obtenido una instancia de DateFormat que tiene a través de un campo estático. Esta parece sospechosa.

Para obtener más información al respecto, vea Sun Error # 6231579 y Error de sol # 6178997.

Y el javadoc for DateFormat sugiere:

formatos de fecha no están sincronizadas. Se recomienda para crear instancias de formato separadas para cada subproceso. Si varios subprocesos acceden a un formato al mismo tiempo, se debe sincronizar externamente.

Jack Leow's answer también tiene un buen punto acerca de la semántica de su uso estático de "TODAY".

De hecho, he visto esto suceder en un entorno de producción de alto tráfico, y es una cosa muy confusa para depurar al principio; entonces, en mi experiencia, la advertencia FindBugs es en realidad una sugerencia útil (a diferencia de algunas otras reglas de análisis estático, que a veces parecen ser insignificantes).

+1

No estoy muy seguro, creo que es demasiado caro crear un SimpleDateFormat cada vez que se llama a myMethod(). – Joset

+1

@eradicus: ¿tiene métricas de rendimiento que demuestren que es "demasiado caro"? A menos que haya observado que es un cuello de botella para la aplicación, preferiría el enfoque local sobre el abarrotar el alcance global con 'finales estáticos '. Obligatorio Knuth: http: // c2.com/cgi/wiki? PrematureOptimization –

+0

¿Por qué no es suficiente que la variable sea "definitiva", es decir, constante? Si no puede cambiar, todos los hilos deberían poder compartirlo, ¿no? ¿O es una cuestión de condiciones de carrera para la inicialización? – kossmoboleat

0

No es seguro para la rosca, por un lado.

4

¿Estás seguro que no es

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

? Eso es lo que indica el mensaje de error.

Creo que lo que pretende es el hecho de que DateFormat no es seguro para subprocesos, por lo que tener una instancia como un campo estático indica las posibles condiciones de carrera.

+0

tienes razón. hay un modificador estático para DateFormat – Joset

0

Supongo que esto se debe a que el formato no es seguro para subprocesos?

(no he visto lo que findbugs se quejan, se puede proporcionar el texto advertencia?)

0

Usted puede conseguir que esto se vaya envolviendo todas las referencias a la DateFormat en un bloque de sincronización - sólo asegúrese todas las llamadas se envuelven en el mismo mismo objeto de sincronización!

+0

Esto podría deshacerse del error FindBugs, pero ¿no sería más lógico simplemente reactivar un DateFormat local cuando lo necesita? –

+0

@Rob, supongo que eso dependerá de si es probable que la sincronización se bloquee de vez en cuando, o si es solo una formalidad para calmar FindBugs. –

+0

Es cierto. Supongo que la sincronización podría ser una alternativa si la sobrecarga de crear un nuevo DateFormat está afectando de alguna manera el rendimiento del sistema, pero dudo que lo haga en la mayoría de los casos. Además, si ese fuera el caso, probablemente sería un sistema de mayor carga, y la sincronización impediría el rendimiento de una manera similar, creo. –

4

No estoy seguro de si FindBugs se queja de esto, pero un problema que veo con su código es que está definiendo TODAY como una variable de nivel de clase (estática), constante (final). Esto comunica la intención que desea que TODAY nunca cambie (no creo que este sea el caso, ya que java.util.Dates es mutable, pero esa es otra historia).

¿Piensa qué pasa si su aplicación se ejecuta durante varios días? TODAY (a menos que lo actualice) hará referencia al día en que se inició la aplicación, no a la fecha actual. ¿Estás seguro de que esto es lo que querías decir?

Esto puede no ser un error en su código en absoluto, pero la intención no es clara, y creo que puede ser de lo que FindBugs se queja.

+0

sí, TODAY no debe cambiar. – Joset

12

Commons Lang tiene un objeto FastDateFormat que es libre de hilos. Sin embargo, solo formatea, no analiza.

Si puede usar commons-lang esto podría funcionar bien para usted.

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

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

Una alternativa que no se ha mencionado es el uso de ThreadLocal. Ver http://www.javacodegeeks.com/2010/07/java-best-practices-dateformat-in.html para más info + rendimiento comparación entre las 3 opciones:

  • Creación de una instancia cada vez
  • Sincronización de acceso
  • Usando ThreadLocal

Ejemplo de uso de ThreadLocal:

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

Uso:

DATE_FORMAT.get().format(TODAY) 
Cuestiones relacionadas