ADDITIONAL SYSTEM INFORMATION :
java -version
java version "24" 2025-03-18
Java(TM) SE Runtime Environment (build 24+36-3646)
Java HotSpot(TM) 64-Bit Server VM (build 24+36-3646, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The DigitList class used in DecimalFormat does not reset the data array in its clone method. This can cause interference when clones are used concurrently.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Save attached test case as DecimalFormatTest.java
Run with "java DecimalFormatTest.java"
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Sample output should report mismatchCount = 0
ACTUAL -
non-zero mismatchCount
---------- BEGIN SOURCE ----------
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class DecimalFormatTest {
static AtomicInteger mismatchCount = new AtomicInteger(0);
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat("#");
String str = df.format(Math.PI); // initial use of formatter
System.out.println(str);
try (var ex = Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory())) {
for (int i = 0; i < 50; i++) {
// each thread gets its own clone of df
DecimalFormat threadDf = (DecimalFormat) df.clone();
ex.execute(makeTask(threadDf));
}
}
System.out.println("mismatchCount = " + mismatchCount);
}
private static Runnable makeTask(DecimalFormat threadDf) {
return () -> {
for (int i = 0; i < 1000000; i++) {
String dfString = threadDf.format(BigDecimal.valueOf(i));
String str = String.valueOf(i);
if (!str.equals(dfString)) {
System.err.println("mismatch: str = " + str + " dfString = " + dfString);
mismatchCount.incrementAndGet();
}
}
};
}
}
---------- END SOURCE ----------
java -version
java version "24" 2025-03-18
Java(TM) SE Runtime Environment (build 24+36-3646)
Java HotSpot(TM) 64-Bit Server VM (build 24+36-3646, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The DigitList class used in DecimalFormat does not reset the data array in its clone method. This can cause interference when clones are used concurrently.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Save attached test case as DecimalFormatTest.java
Run with "java DecimalFormatTest.java"
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Sample output should report mismatchCount = 0
ACTUAL -
non-zero mismatchCount
---------- BEGIN SOURCE ----------
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class DecimalFormatTest {
static AtomicInteger mismatchCount = new AtomicInteger(0);
public static void main(String[] args) {
DecimalFormat df = new DecimalFormat("#");
String str = df.format(Math.PI); // initial use of formatter
System.out.println(str);
try (var ex = Executors.newThreadPerTaskExecutor(Thread.ofPlatform().factory())) {
for (int i = 0; i < 50; i++) {
// each thread gets its own clone of df
DecimalFormat threadDf = (DecimalFormat) df.clone();
ex.execute(makeTask(threadDf));
}
}
System.out.println("mismatchCount = " + mismatchCount);
}
private static Runnable makeTask(DecimalFormat threadDf) {
return () -> {
for (int i = 0; i < 1000000; i++) {
String dfString = threadDf.format(BigDecimal.valueOf(i));
String str = String.valueOf(i);
if (!str.equals(dfString)) {
System.err.println("mismatch: str = " + str + " dfString = " + dfString);
mismatchCount.incrementAndGet();
}
}
};
}
}
---------- END SOURCE ----------
- links to
-
Commit(master) openjdk/jdk/04c32fc0
-
Review(master) openjdk/jdk/24598