2010-11-05 9 views
81

El javadoc para SimpleDateFormat indica que SimpleDateFormat no está sincronizado.Sincronización de acceso a SimpleDateFormat

"Los formatos de fecha no están sincronizadas. Se se recomienda crear instancias formato separados para cada hilo. Si múltiples subprocesos tienen acceso a un formato al mismo tiempo, debe ser sincronizado externamente."

Pero, ¿cuál es el mejor enfoque para utilizar una instancia de SimpleDateFormat en un entorno de múltiples hilos. Aquí hay algunas opciones en las que he pensado, he utilizado las opciones 1 y 2 en el pasado, pero tengo curiosidad por saber si hay alternativas mejores o cuáles de estas opciones ofrecerían el mejor rendimiento y concurrencia.

Opción 1: Crear instancias locales cuando se requiere

public String formatDate(Date d) { 
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
    return sdf.format(d); 
} 

Opción 2: Crear una instancia de SimpleDateFormat como una variable de clase pero sincronizar el acceso a la misma.

private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); 
public String formatDate(Date d) { 
    synchronized(sdf) { 
     return sdf.format(d); 
    } 
} 

Opción 3: Cree un ThreadLocal para almacenar una instancia diferente de SimpleDateFormat para cada subproceso.

private ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>(); 
public String formatDate(Date d) { 
    SimpleDateFormat sdf = tl.get(); 
    if(sdf == null) { 
     sdf = new SimpleDateFormat("yyyy-MM-hh"); 
     tl.set(sdf); 
    } 
    return sdf.format(d); 
} 
+10

+1 para plantear esta cuestión. Mucha gente piensa que SimpleDateFormat es seguro para subprocesos (veo suposiciones en todas partes). –

+0

Para obtener más información sobre el enfoque ThreadLocal, consulte: http://www.javaspecialists.eu/archive/Issue172.html – miner49r

+0

Y para obtener más información, consulte esta pregunta: http://stackoverflow.com/questions/6840803/simpledateformat- seguridad de hilos – Raedwald

Respuesta

40
  1. Creación SimpleDateFormat es expensive. No use esto a menos que se haga rara vez.

  2. OK si puedes vivir con un poco de bloqueo. Úselo si formatDate() no se usa mucho.

  3. Opción más rápida SI reutiliza hilos (thread pool). Utiliza más memoria que 2. y tiene una sobrecarga de inicio más alta.

Para aplicaciones tanto 2. como 3. son opciones viables. Cuál es el mejor para su caso depende de su caso de uso. Tenga cuidado con la optimización prematura. Solo hazlo si crees que esto es un problema.

Para las bibliotecas que serían utilizados por tercera parte que haría uso de la opción 3.

+0

Si usamos la Opción-2 y declaramos 'SimpleDateFormat' como una variable de instancia, entonces podemos usar' synchronized block' para que sea seguro para subprocesos. Pero el sonar muestra una advertencia [squid-AS2885] (https://sonarqube.com/coding_rules#rule_key=squid%3AS2885). ¿Hay alguna forma de resolver el problema del sonar? –

4

No utilice SimpleDateFormat, DateTimeFormatter de uso joda a tiempo en su lugar. Es un poco más estricto en el análisis y, por lo tanto, no reemplaza a SimpleDateFormat, pero joda-time es mucho más amigable en términos de seguridad y rendimiento.

23

La otra opción es Commons Lang FastDateFormat pero solo puede usarla para formatear la fecha y no para analizar.

A diferencia de Joda, puede funcionar como un reemplazo directo para el formateo. (Actualización: Desde V3.3.2, FastDateFormat puede producir un FastDateParser, que es una gota en el reemplazo seguro para subprocesos para SimpleDateFormat)

+8

Desde Commons Lang 3.2, 'FastDateFormat' también tiene el método' parse() ' – manuna

3

diría, crear un sencillo de Clase envolvente para SimpleDateFormat que sincroniza el acceso a analizar () y formato() y se puede utilizar como un reemplazo directo. Más infalible que su opción n. ° 2, menos engorrosa que su opción n. ° 3.

Parece que hacer que SimpleDateFormat no se sincronice fue una decisión de diseño deficiente por parte de los diseñadores de la API de Java; Dudo que alguien espere que format() y parse() necesiten sincronizarse.

0

Imagine que su aplicación tiene un hilo. ¿Por qué sincronizarías el acceso a la variable SimpleDataFormat entonces?

6

Commons Lang 3.x ahora tiene FastDateParser y FastDateFormat. Es seguro y más rápido que SimpleDateFormat. También utiliza las mismas especificaciones de formato de formato/análisis que SimpleDateFormat.

+0

Solo está disponible en 3.2+ y no 3.x – Wisteso

1

Otra opción es mantener las instancias en una cola de flujos seguros:

import java.util.concurrent.ArrayBlockingQueue; 
private static final int DATE_FORMAT_QUEUE_LEN = 4; 
private static final String DATE_PATTERN = "yyyy-MM-dd HH:mm:ss"; 
private ArrayBlockingQueue<SimpleDateFormat> dateFormatQueue = new ArrayBlockingQueue<SimpleDateFormat>(DATE_FORMAT_QUEUE_LEN); 
// thread-safe date time formatting 
public String format(Date date) { 
    SimpleDateFormat fmt = dateFormatQueue.poll(); 
    if (fmt == null) { 
     fmt = new SimpleDateFormat(DATE_PATTERN); 
    } 
    String text = fmt.format(date); 
    dateFormatQueue.offer(fmt); 
    return text; 
} 
public Date parse(String text) throws ParseException { 
    SimpleDateFormat fmt = dateFormatQueue.poll(); 
    if (fmt == null) { 
     fmt = new SimpleDateFormat(DATE_PATTERN); 
    } 
    Date date = null; 
    try { 
     date = fmt.parse(text); 
    } finally { 
     dateFormatQueue.offer(fmt); 
    } 
    return date; 
} 

El tamaño de dateFormatQueue debe ser algo cercano a la estimación del número de hilos que pueden llamar de manera rutinaria esta función al mismo tiempo. En el peor de los casos en que más hilos que este número usen todas las instancias al mismo tiempo, se crearán algunas instancias de SimpleDateFormat que no se pueden devolver a dateFormatQueue porque están llenas. Esto no generará un error, solo incurrirá en la penalidad de crear algún SimpleDateFormat que se use solo una vez.

11

Si está utilizando Java 8, es posible que desee utilizar java.time.format.DateTimeFormatter:

Esta clase es inmutable y seguro para subprocesos.

por ejemplo:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 
String str = new java.util.Date().toInstant() 
           .atZone(ZoneId.systemDefault()) 
           .format(formatter); 
0

acabo implementado este con la opción 3, pero hizo algunos cambios en el código:

  • ThreadLocal por lo general debe ser estática
  • Parece más limpio para anular initialValue () en lugar de probar si (get() == null)
  • Es posible que desee establecer la configuración regional y la hora z uno menos que realmente desea que la configuración por defecto (por defecto son muy propenso a errores con Java)

    private static final ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>() { 
        @Override 
        protected SimpleDateFormat initialValue() { 
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh", Locale.US); 
         sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); 
         return sdf; 
        } 
    }; 
    public String formatDate(Date d) { 
        return tl.get().format(d); 
    } 
    
Cuestiones relacionadas