Maison  >  Article  >  Java  >  Pourquoi le fil de discussion simpledateformat est-il dangereux ?

Pourquoi le fil de discussion simpledateformat est-il dangereux ?

青灯夜游
青灯夜游original
2021-05-10 14:40:2417241parcourir

Cause : dans un environnement multithread, lorsque plusieurs threads utilisent le même objet SimpleDateFormat (comme une modification statique) en même temps, par exemple en appelant la méthode format, plusieurs threads appelleront la méthode calender.setTime en même temps, ce qui entraîne une modification du temps par d'autres threads, de sorte que le thread n'est pas sécurisé.

Pourquoi le fil de discussion simpledateformat est-il dangereux ?

L'environnement d'exploitation de ce tutoriel : système Windows 7, version Java 8, ordinateur DELL G3.

Vérification du fil de discussion non sécurisé :

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

Sortie :

  true
  false
  true
  true
  false

false apparaît, indiquant que le fil de discussion n'est pas sécurisé

1. Comme vous pouvez le voir grâce à la méthode

public StringBuffer format(Date date, StringBuffer toAppendTo,
                               FieldPosition pos)
    {
        pos.beginIndex = pos.endIndex = 0;
        return format(date, toAppendTo, pos.getFieldDelegate());
    }

    // Called from Format after creating a FieldDelegate
    private StringBuffer format(Date date, StringBuffer toAppendTo,
                                FieldDelegate delegate) {
        // Convert input date to time field list
        calendar.setTime(date);

        boolean useDateFormatSymbols = useDateFormatSymbols();

        for (int i = 0; i < compiledPattern.length; ) {
            int tag = compiledPattern[i] >>> 8;
            int count = compiledPattern[i++] & 0xff;
            if (count == 255) {
                count = compiledPattern[i++] << 16;
                count |= compiledPattern[i++];
            }

            switch (tag) {
            case TAG_QUOTE_ASCII_CHAR:
                toAppendTo.append((char)count);
                break;

            case TAG_QUOTE_CHARS:
                toAppendTo.append(compiledPattern, i, count);
                i += count;
                break;

            default:
                subFormat(tag, count, delegate, toAppendTo, useDateFormatSymbols);
                break;
            }
        }
        return toAppendTo;
    }
 protected Calendar calendar;

, le calendrier variable est partagé entre plusieurs threads et le calendrier est modifié. Par conséquent, dans un environnement multithread, lorsque plusieurs threads utilisent le même objet SimpleDateFormat (comme une modification statique) en même temps, par exemple en appelant la méthode format, plusieurs threads appelleront la méthode calender.setTime en même temps, provoquant le temps d'être modifié par d'autres threads. Par conséquent, les threads ne sont pas sûrs.

De plus, la méthode parse est également thread-unsafe. La méthode parse appelle en fait l'établissement de CalenderBuilder pour l'analyse. Les principales étapes de la méthode ne sont pas des opérations atomiques.

Solution :

1. Définir SimpleDateFormat comme variable locale

2. Ajouter un verrou de synchronisation de thread : synchronisé(lock)

 3. En utilisant ThreadLocal, chaque thread possède sa propre copie de l'objet SimpleDateFormat. Tels que :

/**
 * SimpleDateFormat线程安全测试
 * 〈功能详细描述〉
 *
 * @author 17090889
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
public class SimpleDateFormatTest {
        private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<SimpleDateFormat>() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };
    //    private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 100, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(1000), new MyThreadFactory("SimpleDateFormatTest"));

    public void test() {
        while (true) {
            poolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    SimpleDateFormat simpleDateFormat = THREAD_LOCAL.get();
                    if (simpleDateFormat == null) {
                        simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    }
                    String dateString = simpleDateFormat.format(new Date());
                    try {
                        Date parseDate = simpleDateFormat.parse(dateString);
                        String dateString2 = simpleDateFormat.format(parseDate);
                        System.out.println(dateString.equals(dateString2));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    } finally {
                        local.remove();
                    }
                }
            });
        }
    }
}

4. Utilisez DateTimeFormatter au lieu de SimpleDateFormat

DateTimeFormatter est thread-safe et fournit de nombreuses méthodes de formatage par défaut. Des méthodes de formatage personnalisées peuvent également être créées via la méthode ofPattern.

(1) Exemple de format de date :

 LocalDateTime localDateTime = LocalDateTime.now();
 System.out.println(localDateTime); // 2019-11-20T15:04:29.017
 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 String strDate=localDateTime.format(dtf);
 System.out.println(strDate); // 2019/23/20 15:23:46

(2) Analyser la date

 DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
 LocalDateTime localDateTime=LocalDateTime.parse("2019/11/20 15:23:46",dtf);
 System.out.println(localDateTime); // 2019-11-20T15:23:46

Tutoriels vidéo associés recommandés : Tutoriel vidéo Java

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn