Java: The Thread Unsafe SimpleDateFormat

A muchos nos ha pasado que, quizá por la tímida nota del JavaDoc a pie de pagina del SimpleDateFormat (Date formats are not synchronized), hemos visto a la clase morder el polvo, aqui el ejemplo:

  public static void main(final String[] args) throws Exception {
    final SimpleDateFormat t = new SimpleDateFormat("dd-MM-yyyy");
    final String testdata[] = {
        "01-03-1999", "14-02-2001", "31-12-2007"
    };
    final Runnable r[] = new Runnable[testdata.length];
    final Thread th[] = new Thread[testdata.length];
    final long ts = System.currentTimeMillis();
    for (int i = 0; i < r.length; i++) {
      final int i2 = i;
      r[i] = new Runnable() {
        public void run() {
          try {
            for (int j = 0; j < 100000; j++) {
              String str = testdata[i2];
              String str2 = null;
              /* synchronized(t) */{
                Date d = t.parse(str);
                str2 = t.format(d);
              }
              if (!str.equals(str2)) {
                throw new RuntimeException("date conversion failed after " + j
                    + " iterations. Expected " + str + " but got " + str2);
              }
            }
          } catch (ParseException e) {
            throw new RuntimeException("parse failed");
          }
        }
      };
      th[i] = new Thread(r[i]);
      th[i].start();
    }
    for (int i = 0; i < r.length; i++) {
      th[i].join();
    }
  }

Verás un error tal que así:

Exception in thread "Thread-1" Exception in thread "Thread-0" RuntimeException: 
    date conversion failed after 0 iterations. Expected 01-03-1999 but got 11-03-1999
    at TestSimpleDateFormat$1.run(SafeSimpleDateFormat.java:124)
    at java.lang.Thread.run(Thread.java:662)
Exception in thread "Thread-2" java.lang.NumberFormatException: multiple points
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082)
    at java.lang.Double.parseDouble(Double.java:510)
    at java.text.DigitList.getDouble(DigitList.java:151)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1302)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
    at java.text.DateFormat.parse(DateFormat.java:335)
    at TestSimpleDateFormat$1.run(SafeSimpleDateFormat.java:120)
    at java.lang.Thread.run(Thread.java:662)
java.lang.NumberFormatException: multiple points
    at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082)
    at java.lang.Double.parseDouble(Double.java:510)
    at java.text.DigitList.getDouble(DigitList.java:151)
    at java.text.DecimalFormat.parse(DecimalFormat.java:1302)
    at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934)
    at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311)
    at java.text.DateFormat.parse(DateFormat.java:335)
    at TestSimpleDateFormat$1.run(SafeSimpleDateFormat.java:120)
    at java.lang.Thread.run(Thread.java:662)

Las alternativas son usar ThreadLocal o bloques Synchronized, aqui una implementacion de ambas soluciones: Complete Source

Los metodos principales:

public class SafeSimpleDateFormat {
  public static SynchronizedSimpleDateFormat 
        getSynchronizedSimpleDateFormat(final String format) {
    return new SynchronizedSimpleDateFormat(format);
  }

  public static ThreadLocalSimpleDateFormat 
        getThreadLocalSimpleDateFormat(final String format) {
    return new ThreadLocalSimpleDateFormat(format);
  }
  // ...cut...
}

JavaDoc:
JavaDoc: SimpleDateFormat

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: