前几天工作中,遇到一个并发环境下有人写了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
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。