20:40:47 [Thread-4] c.DateFormatTest - {} java.lang.NumberFormatException: For input string: "" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) at java.base/java.lang.Long.parseLong(Long.java:702) at java.base/java.lang.Long.parseLong(Long.java:817) at java.base/java.text.DigitList.getLong(DigitList.java:195) at java.base/java.text.DecimalFormat.parse(DecimalFormat.java:2121) at java.base/java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1933) at java.base/java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1541) at java.base/java.text.DateFormat.parse(DateFormat.java:393) at com.java.demo.immutable.DateFormatTest.lambda$main$0(DateFormatTest.java:15) at java.base/java.lang.Thread.run(Thread.java:834) 20:40:47 [Thread-7] c.DateFormatTest - {} java.lang.NumberFormatException: multiple points at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.base/java.lang.Double.parseDouble(Double.java:543) at java.base/java.text.DigitList.getDouble(DigitList.java:169) at java.base/java.text.DecimalFormat.parse(DecimalFormat.java:2126) at java.base/java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1933) at java.base/java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1541) at java.base/java.text.DateFormat.parse(DateFormat.java:393) at com.java.demo.immutable.DateFormatTest.lambda$main$0(DateFormatTest.java:15) at java.base/java.lang.Thread.run(Thread.java:834) 20:40:47 [Thread-6] c.DateFormatTest - {} java.lang.NumberFormatException: multiple points at java.base/jdk.internal.math.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890) at java.base/jdk.internal.math.FloatingDecimal.parseDouble(FloatingDecimal.java:110) at java.base/java.lang.Double.parseDouble(Double.java:543) at java.base/java.text.DigitList.getDouble(DigitList.java:169) at java.base/java.text.DecimalFormat.parse(DecimalFormat.java:2126) at java.base/java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1933) at java.base/java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1541) at java.base/java.text.DateFormat.parse(DateFormat.java:393) at com.java.demo.immutable.DateFormatTest.lambda$main$0(DateFormatTest.java:15) at java.base/java.lang.Thread.run(Thread.java:834) 20:40:47 [Thread-0] c.DateFormatTest - Sat Apr 21 00:00:00 CST 1951 20:40:47 [Thread-3] c.DateFormatTest - Sat Jan 19 00:00:00 CST 1957 20:40:47 [Thread-1] c.DateFormatTest - Sat Apr 21 00:00:00 CST 1951 20:40:47 [Thread-9] c.DateFormatTest - Mon Apr 21 00:00:00 CST 21 20:40:47 [Thread-5] c.DateFormatTest - Sat Apr 21 00:00:00 CST 1951 20:40:47 [Thread-8] c.DateFormatTest - Sat Apr 21 00:00:00 CST 1951 20:40:47 [Thread-2] c.DateFormatTest - Mon Apr 21 00:00:00 CST 2121
问题解决-同步锁
虽能解决问题,但带来的是性能上的损失,并不算很好:
1 2 3 4 5 6 7 8 9 10 11 12
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); for (int i = 0; i < 50; i++) { new Thread(() -> { synchronized (sdf) { try { log.debug("{}", sdf.parse("1951-04-21")); } catch (Exception e) { log.error("{}", e); } } }).start(); }
/** * The value is used for character storage. * * @implNote This field is trusted by the VM, and is a subject to * constant folding if String instance is constant. Overwriting this * field after construction will cause problems. * * Additionally, it is marked with {@link Stable} to trust the contents * of the array. No other facility in JDK provides this functionality (yet). * {@link Stable} is safe here, because value is never null. */ @Stable privatefinalbyte[] value;
/** * The identifier of the encoding used to encode the bytes in * {@code value}. The supported values in this implementation are * * LATIN1 * UTF16 * * @implNote This field is trusted by the VM, and is a subject to * constant folding if String instance is constant. Overwriting this * field after construction will cause problems. */ privatefinalbyte coder;
/** Cache the hash code for the string */ privateint hash; }
/** * Returns a string that is a substring of this string. The * substring begins with the character at the specified index and * extends to the end of this string. <p> * Examples: * <blockquote><pre> * "unhappy".substring(2) returns "happy" * "Harbison".substring(3) returns "bison" * "emptiness".substring(9) returns "" (an empty string) * </pre></blockquote> * * @param beginIndex the beginning index, inclusive. * @return the specified substring. * @exception IndexOutOfBoundsException if * {@code beginIndex} is negative or larger than the * length of this {@code String} object. */ public String substring(int beginIndex){ if (beginIndex < 0) { thrownew StringIndexOutOfBoundsException(beginIndex); } int subLen = length() - beginIndex; if (subLen < 0) { thrownew StringIndexOutOfBoundsException(subLen); } if (beginIndex == 0) { returnthis; } return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) : StringUTF16.newString(value, beginIndex, subLen); }
发现其内部就是调用 String 的构造方法创建一个新字符串,再看看这个构造,是否对 final char[] value 做出了修改:
publicPool(int poolSize){ this.poolSize = poolSize; this.connections = new Connection[poolSize]; this.states = new AtomicIntegerArray(newint[poolSize]); for (int i = 0; i < poolSize; i++) { connections[i] = new MockConnection("连接" + (i + 1)); } }