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é.
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!