Question

Le javadoc pour SimpleDateFormat indique que SimpleDateFormat ne sont pas synchronisées.

  

"Les formats de date ne sont pas synchronisés. Il   il est recommandé de créer séparée   les instances de format pour chaque thread. Si   plusieurs threads accès format   en même temps, il doit être synchronisé   à l'extérieur. "

Mais quelle est la meilleure approche pour utiliser une instance de SimpleDateFormat dans un environnement multithread. Voici quelques options que j'ai pensé, je l'ai utilisé les options 1 et 2 dans le passé, mais je suis curieux de savoir s'il y a des meilleures alternatives ou qui de ces options offrent les meilleures performances et de la concurrence.

Option 1: Créer des instances locales lorsque cela est nécessaire

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

Option 2:. Créer une instance de SimpleDateFormat comme une variable de classe, mais synchroniser l'accès à ce

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

Option 3:. Créer un ThreadLocal pour stocker une autre instance de SimpleDateFormat pour chaque thread

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);
}
Était-ce utile?

La solution

  1. Création d'SimpleDateFormat est cher. Ne pas utiliser ce à moins que cela se fait rarement.

  2. OK si vous pouvez vivre avec un peu de blocage. Utiliser si formatDate () ne sert pas beaucoup.

  3. option la plus rapide Si vous réutilisez threads ( pool de threads ). Utilisations plus de mémoire que 2 et dispose d'une charge de démarrage supérieur.

Pour les applications à la fois 2. et 3. sont des options viables. Ce qui est le mieux pour votre cas dépend de votre cas d'utilisation. Méfiez-vous de l'optimisation prématurée. Seulement faire si vous croyez que cela est un problème.

Pour les bibliothèques qui seraient utilisées par 3ème partie j'utiliser l'option 3.

Autres conseils

L'autre option est Commons Lang FastDateFormat , mais vous ne pouvez l'utiliser pour le formatage et la date pas l'analyse syntaxique.

Contrairement à Joda, il peut fonctionner comme une solution de remplacement pour le formatage. (Mise à jour: Depuis V3.3.2, FastDateFormat peut produire un FastDateParser , qui est un remplacement de thread-safe drop-in pour SimpleDateFormat)

Si vous utilisez Java 8, vous pouvez utiliser < strong> java.time.format.DateTimeFormatter :

  

Cette classe est immuable et thread-safe.

par exemple:.

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

Commons 3.x Lang a maintenant FastDateParser ainsi que FastDateFormat. Il est sûr de fil et plus rapide que SimpleDateFormat. Il utilise le même format / parse spécifications de motif que SimpleDateFormat.

Ne pas utiliser SimpleDateFormat, DateTimeFormatter de l'utilisation Joda-temps à la place. Il est un peu plus stricte du côté de l'analyse syntaxique et est donc pas tout à fait le remplacement des la SimpleDateFormat, mais Joda-temps est beaucoup plus convivial concurrent en termes de sécurité et de performance.

Je dirais, créer un simple wrapper de classe pour SimpleDateFormat qui synchronise l'accès à parse () et le format () et peut être utilisé en remplacement sans rendez-vous. Plus à toute épreuve que votre option # 2, moins encombrant que votre option # 3.

On dirait fait SimpleDateFormat désynchronisé était une décision de mauvaise conception de la part des concepteurs de l'API Java; Je doute que quelqu'un format attend () et parse () à doivent être synchronisés.

Une autre option est de garder les instances dans une file d'attente thread-safe:

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

La taille de dateFormatQueue devrait être quelque chose proche du nombre estimé de fils qui peuvent appeler régulièrement cette fonction en même temps. Dans le pire des cas où plus de threads que ce nombre n'utilisent effectivement toutes les instances en même temps, certains cas SimpleDateFormat seront créés qui ne peuvent être retournés à dateFormatQueue parce qu'il est plein. Cela ne génère pas d'erreur, il va juste encourir la peine de créer une SimpleDateFormat qui sont utilisés qu'une seule fois.

Je viens avec l'option mis en œuvre cette 3, mais fait quelques changements de code:

  • ThreadLocal doit généralement être statique
  • semble plus propre à remplacer initialValue () au lieu de test si (get () == NULL)
  • Vous pouvez vouloir locale jeu et le fuseau horaire, sauf si vous voulez vraiment les paramètres par défaut (par défaut sont des erreurs très sujettes à 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);
    }
    

Imaginez votre application a un fil. Pourquoi voudriez-vous synchroniser l'accès à la variable SimpleDateFormat alors?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top