前几天工作中,遇到一个并发环境下有人写了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 th2 = 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 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 = "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 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 = "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 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 = "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 th6 = 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) {                }            }        }    });    th2.start();    th3.start();    th4.start();    th5.start();    th6.start();    try {        th2.join();        th3.join();        th4.join();        th5.join();        th6.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对象打印出来是同一个,但其实不是。


狄仁杰:元芳,我这个宰相的位置,以后要不你来做吧。。。


-----------------结束的分割线-----------------

之前我写在CSDN上的文章,搬到平台上来:

如果觉得不错,请赞咱一下呗

原文链接:https://blog.csdn.net/qq_35039122/article/details/80172714