/*
*******************************************************************************
-* Copyright (C) 2007-2011, International Business Machines
+* Copyright (C) 2007-2014, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
*/
/*
* NumberFormat implementation dedicated/optimized for DateFormat,
* used by SimpleDateFormat implementation.
+ * This class is not thread-safe.
*/
public final class DateNumberFormat extends NumberFormat {
private char minusSign;
private boolean positiveOnly = false;
- private transient char[] decimalBuf = new char[20]; // 20 digits is good enough to store Long.MAX_VALUE
+ private static final int DECIMAL_BUF_SIZE = 20; // 20 digits is good enough to store Long.MAX_VALUE
+ private transient char[] decimalBuf = new char[DECIMAL_BUF_SIZE];
private static SimpleCache<ULocale, char[]> CACHE = new SimpleCache<ULocale, char[]>();
}
public char[] getDigits() {
- return digits;
+ return digits.clone();
}
public StringBuffer format(double number, StringBuffer toAppendTo,
setZeroDigit(zeroDigit);
}
// re-allocate the work buffer
- decimalBuf = new char[20];
+ decimalBuf = new char[DECIMAL_BUF_SIZE];
+ }
+
+ @Override
+ public Object clone() {
+ DateNumberFormat dnfmt = (DateNumberFormat)super.clone();
+ dnfmt.digits = this.digits.clone();
+ dnfmt.decimalBuf = new char[DECIMAL_BUF_SIZE];
+ return dnfmt;
}
}
}
}
+ /*
+ * Initializes transient fields for fast simple numeric formatting
+ * code. This method should be called whenever number format is updated.
+ */
private void initLocalZeroPaddingNumberFormat() {
if (numberFormat instanceof DecimalFormat) {
decDigits = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getDigits();
}
if (useLocalZeroPaddingNumberFormat) {
- decimalBuf = new char[10]; // sufficient for int numbers
+ decimalBuf = new char[DECIMAL_BUF_SIZE];
}
}
// If true, use local version of zero padding number format
private transient boolean useLocalZeroPaddingNumberFormat;
- private transient char[] decDigits;
- private transient char[] decimalBuf;
+ private transient char[] decDigits; // read-only - can be shared by multiple instances
+ private transient char[] decimalBuf; // mutable - one per instance
+ private static final int DECIMAL_BUF_SIZE = 10; // sufficient for int numbers
/*
* Lightweight zero padding integer number format function.
public Object clone() {
SimpleDateFormat other = (SimpleDateFormat) super.clone();
other.formatData = (DateFormatSymbols) formatData.clone();
+ // We must create a new copy of work buffer used by
+ // the fast numeric field format code.
+ if (this.decimalBuf != null) {
+ other.decimalBuf = new char[DECIMAL_BUF_SIZE];
+ }
return other;
}
}
}
-
+
+ // Test case for numeric field format threading problem
+ public void TestT11363() {
+
+ class TestThread extends Thread {
+ SimpleDateFormat fmt;
+ Date d;
+
+ TestThread(SimpleDateFormat fmt, Date d) {
+ this.fmt = fmt;
+ this.d = d;
+ }
+
+ public void run() {
+ String s0 = fmt.format(d);
+ for (int i = 0; i < 1000; i++) {
+ String s = fmt.format(d);
+ if (!s0.equals(s)) {
+ errln("Result: " + s + ", Expected: " + s0);
+ }
+ }
+ }
+ }
+
+ SimpleDateFormat fmt0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
+ Thread[] threads = new Thread[10];
+
+ GregorianCalendar cal = new GregorianCalendar(2014, Calendar.NOVEMBER, 5, 12, 34, 56);
+ cal.set(Calendar.MILLISECOND, 777);
+
+ // calls format() once on the base object to trigger
+ // lazy initialization stuffs.
+ fmt0.format(cal.getTime());
+
+ for (int i = 0; i < threads.length; i++) {
+ // Add 1 to all fields to use different numbers in each thread
+ cal.add(Calendar.YEAR, 1);
+ cal.add(Calendar.MONTH, 1);
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+ cal.add(Calendar.HOUR_OF_DAY, 1);
+ cal.add(Calendar.MINUTE, 1);
+ cal.add(Calendar.SECOND, 1);
+ cal.add(Calendar.MILLISECOND, 1);
+ Date d = cal.getTime();
+ SimpleDateFormat fmt = (SimpleDateFormat)fmt0.clone();
+ threads[i] = new TestThread(fmt, d);
+ }
+
+ for (Thread t : threads) {
+ t.start();
+ }
+ }
}