-
Bug
-
Resolution: Fixed
-
P4
-
17, 25
-
master
-
generic
-
generic
A DESCRIPTION OF THE PROBLEM :
When using instances from CompactNumberFormat.clone across threads, the instances interfere with each other because internal state is still shared.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Save the attached code to CompactFormatCloneTest.java
2. run with java CompactFormatCloneTest.java
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No output
ACTUAL -
Output (varies per run)
parseClone 2: expected 2 but got 5
parseClone 7: expected 7 but got 9
formatClone 4: exceptionFor input string: ".4E14E1"
formatClone 6: exceptionFor input string: ".6E16E1"
parseClone 8: expected 8 but got 5
parseClone 3: expected 3 but got 1
parseClone 4: expected 4 but got 1
formatClone 3: exceptionFor input string: ".E0"
formatClone 9: exceptionFor input string: ".E0"
parseClone 5: expected 5 but got 1
parseClone 0: expected 0 but got 1
formatClone 2: exceptionFor input string: ".2E2E1"
parseClone 9: expected 9 but got 1
formatClone 7: exceptionFor input string: ".7E7E1"
parseClone 6: expected 6 but got 1
formatClone 8: exceptionFor input string: ".8E8E1"
formatClone 5: exceptionFor input string: ".5E15E1"
---------- BEGIN SOURCE ----------
import java.text.NumberFormat;
import java.util.Locale;
public class CompactFormatCloneTest {
public static void main(String[] args) {
// successful format loop does not use clone()
for (int i = 0; i < 10; i++) {
// give each thread a fresh copy of the format
NumberFormat instance = createFormat();
new Thread(new FormatTest(i, instance), "freshFormat " + i).start();
}
NumberFormat originalInstance = createFormat();
// unsuccessful format loop using clone()
for (int i = 0; i < 10; i++) {
// give each thread a clone of the original
NumberFormat instance = (NumberFormat) originalInstance.clone();
new Thread(new FormatTest(i, instance), "formatClone " + i).start();
}
// unsuccessful parse loop using clone()
for (int i = 0; i < 10; i++) {
// give each thread a clone of the original
NumberFormat instance = (NumberFormat) originalInstance.clone();
new Thread(new ParseTest(i, instance), "parseClone " + i).start();
}
}
public record FormatTest(int value, NumberFormat format) implements Runnable {
@Override
public void run() {
String expected = String.valueOf(value);
for (int i = 0; i < 1000; i++) {
try {
String result = format.format(value);
if (!result.equals(expected)) {
System.out.println(Thread.currentThread().getName() + ": expected " + expected + " but got " + result);
break;
}
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + ": exception" + e.getMessage());
break;
}
}
}
}
public record ParseTest(int value, NumberFormat format) implements Runnable {
@Override
public void run() {
String str = String.valueOf(value);
for (int i = 0; i < 1000; i++) {
try {
int result = format.parse(str).intValue();
if (result != value) {
System.out.println(Thread.currentThread().getName() + ": expected " + value + " but got " + result);
break;
}
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + ": exception" + e.getMessage());
break;
}
}
}
}
private static NumberFormat createFormat() {
return NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
}
}
---------- END SOURCE ----------
When using instances from CompactNumberFormat.clone across threads, the instances interfere with each other because internal state is still shared.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Save the attached code to CompactFormatCloneTest.java
2. run with java CompactFormatCloneTest.java
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No output
ACTUAL -
Output (varies per run)
parseClone 2: expected 2 but got 5
parseClone 7: expected 7 but got 9
formatClone 4: exceptionFor input string: ".4E14E1"
formatClone 6: exceptionFor input string: ".6E16E1"
parseClone 8: expected 8 but got 5
parseClone 3: expected 3 but got 1
parseClone 4: expected 4 but got 1
formatClone 3: exceptionFor input string: ".E0"
formatClone 9: exceptionFor input string: ".E0"
parseClone 5: expected 5 but got 1
parseClone 0: expected 0 but got 1
formatClone 2: exceptionFor input string: ".2E2E1"
parseClone 9: expected 9 but got 1
formatClone 7: exceptionFor input string: ".7E7E1"
parseClone 6: expected 6 but got 1
formatClone 8: exceptionFor input string: ".8E8E1"
formatClone 5: exceptionFor input string: ".5E15E1"
---------- BEGIN SOURCE ----------
import java.text.NumberFormat;
import java.util.Locale;
public class CompactFormatCloneTest {
public static void main(String[] args) {
// successful format loop does not use clone()
for (int i = 0; i < 10; i++) {
// give each thread a fresh copy of the format
NumberFormat instance = createFormat();
new Thread(new FormatTest(i, instance), "freshFormat " + i).start();
}
NumberFormat originalInstance = createFormat();
// unsuccessful format loop using clone()
for (int i = 0; i < 10; i++) {
// give each thread a clone of the original
NumberFormat instance = (NumberFormat) originalInstance.clone();
new Thread(new FormatTest(i, instance), "formatClone " + i).start();
}
// unsuccessful parse loop using clone()
for (int i = 0; i < 10; i++) {
// give each thread a clone of the original
NumberFormat instance = (NumberFormat) originalInstance.clone();
new Thread(new ParseTest(i, instance), "parseClone " + i).start();
}
}
public record FormatTest(int value, NumberFormat format) implements Runnable {
@Override
public void run() {
String expected = String.valueOf(value);
for (int i = 0; i < 1000; i++) {
try {
String result = format.format(value);
if (!result.equals(expected)) {
System.out.println(Thread.currentThread().getName() + ": expected " + expected + " but got " + result);
break;
}
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + ": exception" + e.getMessage());
break;
}
}
}
}
public record ParseTest(int value, NumberFormat format) implements Runnable {
@Override
public void run() {
String str = String.valueOf(value);
for (int i = 0; i < 1000; i++) {
try {
int result = format.parse(str).intValue();
if (result != value) {
System.out.println(Thread.currentThread().getName() + ": expected " + value + " but got " + result);
break;
}
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + ": exception" + e.getMessage());
break;
}
}
}
}
private static NumberFormat createFormat() {
return NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
}
}
---------- END SOURCE ----------
- links to
-
Commit(master) openjdk/jdk/bdf6853c
-
Review(master) openjdk/jdk/27475