疑惑,测试SimpleDateFormat并发的代码执行结果很奇怪。。。

xiaoxiao2021-02-28  34

前几天工作中,遇到一个并发环境下有人写了SimpleDateFormat的场景,印象中这个是不能支持多线程的,应该使用ThreadLocal作为每个线程局部变量使用,今天有空,试了下SimpleDateFormat多线程使用,代码如下:

/** * TestDateFormat.java * zhm.test.dateFormat * 2018年5月2日下午9:02:07 * */ package zhm.test.dateFormat; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Random; /** * @author zhuheming TestDateFormat 2018年5月2日下午9:02:07 */ public class TestDateFormat { public static TestDateFormat tdf = null; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); public static void main(String[] args) { tdf = new TestDateFormat(); for (int i = 1; i <= 9; i++) { Thread th = new Thread(new Runnable() { @Override public void run() { while (true) { Random r = new Random(); int k = r.nextInt(100); int randomSwitch = k % 3; Calendar c = Calendar.getInstance(); c.add(Calendar.DATE, k); try { Thread.sleep(100); switch (randomSwitch) { case 0: String s1 = c.get(Calendar.YEAR) + "-" + TestDateFormat.formatNumber((c.get(Calendar.MONTH) + 1)) + "-" + TestDateFormat.formatNumber((c.get(Calendar.DAY_OF_MONTH))); Date date1 = tdf.printDateFormat(s1); String d1 = new Timestamp(date1.getTime()).toString(); d1 = d1.substring(0,d1.indexOf(" ")); if(!s1.equals(d1)){ System.out.println(s1+" "+d1); } break; case 1: String s2 = c.get(Calendar.YEAR) + "-" + TestDateFormat.formatNumber((c.get(Calendar.MONTH) + 1)) + "-" + TestDateFormat.formatNumber((c.get(Calendar.DAY_OF_MONTH))); Date date2 = TestDateFormat.printStaticDateFormat(s2); String d2 = new Timestamp(date2.getTime()).toString(); d2 = d2.substring(0,d2.indexOf(" ")); if(!s2.equals(d2)){ System.out.println(s2+" "+d2); } break; case 2: String s = c.get(Calendar.YEAR) + "-" + TestDateFormat.formatNumber((c.get(Calendar.MONTH) + 1)) + "-" + TestDateFormat.formatNumber((c.get(Calendar.DAY_OF_MONTH))); Date date3 = tdf.printReferenceDateFormat(s); String d3 = new Timestamp(date3.getTime()).toString(); d3 = d3.substring(0,d3.indexOf(" ")); if(!s.equals(d3)){ System.out.println(s+" "+d3); } break; default: return; } } catch (Exception e) { } } } }); th.start(); try { th.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public Date printReferenceDateFormat(String str) { System.out.println("get the ReferenceDateFormat:" + sdf); try { return sdf.parse(str); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } public static Date printStaticDateFormat(String str) { SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd"); System.out.println("get static method the SimpleDateFormat:" + df1); try { return df1.parse(str); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } public Date printDateFormat(String str) { SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd"); try { System.out.println("get the SimpleDateFormat:" + df2); return df2.parse(str); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } private static String formatNumber(int n) { String s = "0" + n; return s.substring(s.length() - 2); } }

按照设想,应该是sdf打印出的对象是同一个,后面两个打印出的对象因为每次需要新建对象,打印出来的都不相同才对。

但是结果如下: get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get static method the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200 get the SimpleDateFormat:java.text.SimpleDateFormat@f67a0200 get the ReferenceDateFormat:java.text.SimpleDateFormat@f67a0200

对象居然是同一个,这就很奇怪了,谁知道原因呢?

看了下SimpleDateFormat源码,里面根据pattern从static的concurrentHashMap中取对应的信息,对于不同的SimpleDateFormat对象,只要定义的格式相同,pattern对象是同一个。

后来换了种方式测试,直接粗暴的写五个进程跑,证明确实会出现并发问题: 2018-12-25 2203-01-24 2498-10-10 2203-01-24 2018-12-25 0001-10-10 2998-01-02 3004-12-01 1698-09-22 3004-12-01 且只有共享的static会有这个问题,去掉共享的static,那么在方法内调用的就不存在并发问题。

/** * TestDateFormat.java * zhm.test.dateFormat * 2018年5月2日下午9:02:07 * */ package zhm.test.dateFormat; import java.sql.Timestamp; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Random; /** * @author zhuheming TestDateFormat 2018年5月2日下午9:02:07 */ public class TestDateFormat { public static TestDateFormat tdf = null; //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); public static void main(String[] args) { tdf = new TestDateFormat(); Thread th1 = new Thread(new Runnable() { @Override public void run() { while (true) { Random r = new Random(); int k = r.nextInt(100); int randomSwitch = k % 3; //Calendar c = Calendar.getInstance(); //c.add(Calendar.DATE, k); try { Thread.sleep(100); switch (randomSwitch) { case 0: String s1 = "2018-12-25"; String d1 = tdf.printDateFormat(s1); //String d1 = new Timestamp(date1.getTime()).toString(); //d1 = d1.substring(0,d1.indexOf(" ")); if(!s1.equals(d1)){ System.out.println(s1+" "+d1); } break; case 1: String s2 = "2018-12-25"; String d2 = TestDateFormat.printStaticDateFormat(s2); //String d2 = new Timestamp(date2.getTime()).toString(); //d2 = d2.substring(0,d2.indexOf(" ")); if(!s2.equals(d2)){ System.out.println(s2+" "+d2); } break; case 2: String s ="2018-12-25"; String d3 = tdf.printReferenceDateFormat(s); //String d3 = new Timestamp(date3.getTime()).toString(); //d3 = d3.substring(0,d3.indexOf(" ")); if(!s.equals(d3)){ System.out.println(s+" "+d3); } break; default: return; } } catch (Exception e) { } } } }); Thread th2 = new Thread(new Runnable() { @Override public void run() { while (true) { Random r = new Random(); int k = r.nextInt(3); int randomSwitch = k % 3; //Calendar c = Calendar.getInstance(); //c.add(Calendar.DATE, k); try { Thread.sleep(100); switch (randomSwitch) { case 0: String s1 = "1998-11-12"; String d1 = tdf.printDateFormat(s1); //String d1 = new Timestamp(date1.getTime()).toString(); //d1 = d1.substring(0,d1.indexOf(" ")); if(!s1.equals(d1)){ System.out.println(s1+" "+d1); } break; case 1: String s2 = "1998-11-12"; String d2 = TestDateFormat.printStaticDateFormat(s2); //String d2 = new Timestamp(date2.getTime()).toString(); //d2 = d2.substring(0,d2.indexOf(" ")); if(!s2.equals(d2)){ System.out.println(s2+" "+d2); } break; case 2: String s = "1998-11-12"; String d3 = tdf.printReferenceDateFormat(s); //String d3 = new Timestamp(date3.getTime()).toString(); //d3 = d3.substring(0,d3.indexOf(" ")); if(!s.equals(d3)){ System.out.println(s+" "+d3); } break; default: return; } } catch (Exception e) { } } } }); Thread th3 = new Thread(new Runnable() { @Override public void run() { while (true) { Random r = new Random(); int k = r.nextInt(3); int randomSwitch = k % 3; //Calendar c = Calendar.getInstance(); //c.add(Calendar.DATE, k); try { Thread.sleep(100); switch (randomSwitch) { case 0: String s1 = "1698-09-22"; String d1 = tdf.printDateFormat(s1); //String d1 = new Timestamp(date1.getTime()).toString(); //d1 = d1.substring(0,d1.indexOf(" ")); if(!s1.equals(d1)){ System.out.println(s1+" "+d1); } break; case 1: String s2 = "1698-09-22"; String d2 = TestDateFormat.printStaticDateFormat(s2); //String d2 = new Timestamp(date2.getTime()).toString(); //d2 = d2.substring(0,d2.indexOf(" ")); if(!s2.equals(d2)){ System.out.println(s2+" "+d2); } break; case 2: String s = "1698-09-22"; String d3 = tdf.printReferenceDateFormat(s); //String d3 = new Timestamp(date3.getTime()).toString(); //d3 = d3.substring(0,d3.indexOf(" ")); if(!s.equals(d3)){ System.out.println(s+" "+d3); } break; default: return; } } catch (Exception e) { } } } }); Thread th4 = new Thread(new Runnable() { @Override public void run() { while (true) { Random r = new Random(); int k = r.nextInt(3); int randomSwitch = k % 3; //Calendar c = Calendar.getInstance(); //c.add(Calendar.DATE, k); try { Thread.sleep(100); switch (randomSwitch) { case 0: String s1 = "2498-10-10"; String d1 = tdf.printDateFormat(s1); //String d1 = new Timestamp(date1.getTime()).toString(); //d1 = d1.substring(0,d1.indexOf(" ")); if(!s1.equals(d1)){ System.out.println(s1+" "+d1); } break; case 1: String s2 = "2498-10-10"; String d2 = TestDateFormat.printStaticDateFormat(s2); //String d2 = new Timestamp(date2.getTime()).toString(); //d2 = d2.substring(0,d2.indexOf(" ")); if(!s2.equals(d2)){ System.out.println(s2+" "+d2); } break; case 2: String s = "2498-10-10"; String d3 = tdf.printReferenceDateFormat(s); //String d3 = new Timestamp(date3.getTime()).toString(); //d3 = d3.substring(0,d3.indexOf(" ")); if(!s.equals(d3)){ System.out.println(s+" "+d3); } break; default: return; } } catch (Exception e) { } } } }); Thread th5 = new Thread(new Runnable() { @Override public void run() { while (true) { Random r = new Random(); int k = r.nextInt(3); int randomSwitch = k % 3; //Calendar c = Calendar.getInstance(); //c.add(Calendar.DATE, k); try { Thread.sleep(100); switch (randomSwitch) { case 0: String s1 = "2998-01-02"; String d1 = tdf.printDateFormat(s1); //String d1 = new Timestamp(date1.getTime()).toString(); //d1 = d1.substring(0,d1.indexOf(" ")); if(!s1.equals(d1)){ System.out.println(s1+" "+d1); } break; case 1: String s2 = "2998-01-02"; String d2 = TestDateFormat.printStaticDateFormat(s2); //String d2 = new Timestamp(date2.getTime()).toString(); //d2 = d2.substring(0,d2.indexOf(" ")); if(!s2.equals(d2)){ System.out.println(s2+" "+d2); } break; case 2: String s = "2998-01-02"; String d3 = tdf.printReferenceDateFormat(s); //String d3 = new Timestamp(date3.getTime()).toString(); //d3 = d3.substring(0,d3.indexOf(" ")); if(!s.equals(d3)){ System.out.println(s+" "+d3); } break; default: return; } } catch (Exception e) { } } } }); th1.start(); th2.start(); th3.start(); th4.start(); th5.start(); try { th1.join(); th2.join(); th3.join(); th4.join(); th5.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public String printReferenceDateFormat(String str) { //Date dt= sdf.parse(str); //return sdf.format(dt); return str; } public static String printStaticDateFormat(String str) { SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd"); //System.out.println("get static method the SimpleDateFormat:" + df1); try { Thread.sleep(100); Date dt=df1.parse(str); str=df1.format(dt); //df1.getCalendar().clear(); return str; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } public String printDateFormat(String str) { SimpleDateFormat df2 = new SimpleDateFormat("yyyy-MM-dd"); try { Thread.sleep(100); Date dt=df2.parse(str); str=df2.format(dt); //df2.getCalendar().clear(); //System.out.println("get the SimpleDateFormat:" + df2); return str; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); return null; } } }

那么只有最后一个问题了,为什么看起来是同一个对象呢:

大家都知道system.out.prinl(object)其实是打印的String.valueof的对象值;valueof是什么呢? public static String valueOf(Object obj) { return (obj == null) ? “null” : obj.toString(); } 就是obj的toString的值,那么obj的toString由Object类定义,各个子类自己重写,在Object中,定义如下: public String toString() { return getClass().getName() + “@” + Integer.toHexString(hashCode()); }

显然就是Object的hashCode的十六进制数值,那么在SimpleDateFormat中hashcode方法是如何呢? public int hashCode() { return pattern.hashCode(); // just enough fields for a reasonable distribution } 从这里可以看出,SimpleDateFormat没有返回自己的对象的地址等,而是返回了pattern的hashcode,前面已经说过,输入的格式相同,pattern是同一个,所以,此处SimpleDateFormat对象打印出来是同一个,但其实不是。

每次看源码,带着疑问看源代码,总是能让人收获很多,大家共勉。。。

转载请注明原文地址: https://www.6miu.com/read-2622611.html

最新回复(0)