Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4101500

java.text.NumberFormat is not thread safe

    XMLWordPrintable

Details

    • 1.1.6
    • sparc
    • solaris_2.5.1
    • Verified

    Backports

      Description



        Name: ccC48265 Date: 12/29/97

        IN BRIEF:
        The JDK's java.text.NumberFormat class and its subclasses are not
        thread-safe. The act of formatting a number actually stores some
        transient state in the formatting object itself. If simultaneous threads
        attempt to use the same formatting object to format a number, the
        object can get confused and throw all kinds of runtime exceptions,
        mostly of the StringIndexOutOfBounds variety.

        We noticed this in JDK 1.1.4 and I confirmed with a diff of the source
        files between 1.2 and 1.1.4 (the only thing changed are comments).

        BACKGROUND AND TEST CASE:
        The bug is self-evident from examining the code in java.text.DecimalFormat's
        format() method.

            // Overrides
            public StringBuffer format(double number, StringBuffer result,
                                       FieldPosition fieldPosition)
            {
                // Initialize
                fieldPosition.beginIndex = fieldPosition.endIndex = 0;

                if (Double.isNaN(number)) {
                    result.append(symbols.getNaN());
                } else {
                    boolean isNegative = (number < 0);
                    if (!isNegative)
                        result.append(positivePrefix);
                    else {
                        result.append(negativePrefix);
                        number = -number;
                    }
                    if (Double.isInfinite(number)) {
                        result.append(symbols.getInfinity());
                    } else {
                        if (multiplier != 1) number *= multiplier;
                        digitList.set(number, getMaximumFractionDigits());
                        appendNativeDigits(result, fieldPosition);
                    }
                    if (!isNegative)
                        result.append(positiveSuffix);
                    else result.append(negativeSuffix);
                }
                return result;
            }

        In the above, digitList is a member variable of the DecimalFormat object.
        This code is manipulating the state of the member variable in
        digitList.set(...); this state is later consulted in the
        appendNativeDigits() method. Obviously, using member variable state to
        communicate between these methods is not thread-safe.

        We have written you this small test program, which will occasionally
        barf with OutOfArrayExceptions. But it doesn't do it very often, as is
        the case with threading problems sometimes. ;) So you might need to just
        look at the "logic problem" we've pointed out above.

        e.g. % java NumberFormatTest 1000

        import java.text.NumberFormat;

        public class NumberFormatTest {

          static final NumberFormat sForm = NumberFormat.getCurrencyInstance();

          public static void main(String [] pArgs) {
            sForm.setMinimumFractionDigits(2);
            sForm.setMaximumFractionDigits(2);

            // do formatting in many threads:
            for (int i = 0; i < Integer.parseInt(pArgs[0]); i++) {
              Runnable r = null;

              switch (i % 3) {
              case 0:
                r = new Runnable() {
                  public void run() {
                      while(true) {
                        try {
                          sForm.format(10.00);
                        }
                        catch (Exception exc) {
                          System.out.println(exc);
                        }
                      }
                  }
                };
                break;

              case 1:
                r = new Runnable() {
                  public void run() {
                      while(true) {
                        try {
                          sForm.format(1.00);
                        }
                        catch (Exception exc) {
                          System.out.println(exc);
                        }
                      }
                  }
                };
                break;

              case 2:
                r = new Runnable() {
                  public void run() {
                      while(true) {
                        try {
                          sForm.format(0.00);
                        }
                        catch (Exception exc) {
                          System.out.println(exc);
                        }
                      }
                  }
                };
                break;

              }

              System.out.println("starting thread number " + i);
              new Thread(r).start();
            }
          }

        }
        ================================================================
        ###@###.### (Dec 29, 1997):
        Using JDK1.2beta2, I was UNable to reproduce this bug:
        I tried to run the test case many times, but no exceptions
        were thrown...

        However, with this application (as with many threading
        applications) there is no guarantee that an exception will
        be thrown. More importantly, though, the submitter brings
        up compelling arguments why something is fundamentally wrong
        with the code for java.text.DecimalFormat's format(double,
        StringBuffer, FieldPosition) method. This would justify
        a review of the code.
        (Review ID: 22011)
        ======================================================================

        Attachments

          Issue Links

            Activity

              People

                joconnersunw John Oconner (Inactive)
                ccresswesunw Claudette Cresswell (Inactive)
                Votes:
                0 Vote for this issue
                Watchers:
                0 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved:
                  Imported:
                  Indexed: