Java

当前位置:首页 > Java

Java之SimpleDateFormat - Java

在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修...

在多线程环境下,当多个线程同时使用相同的SimpleDateFormat对象(如static修饰)的话,如调用format方法时,多个线程会同时调用calender.setTime方法,导致time被别的线程修改,因此线程是不安全的。

SimpleDateFormat类内部有一个Calendar对象引用,它用来储存和这个SimpleDateFormat相关的日期信息,例如sdf.parse(dateStr),sdf.format(date) 诸如此类的方法参数传入的日期相关String,Date等等, 都是交由Calendar引用来储存的.这样就会导致一个问题,如果你的SimpleDateFormat是个static的, 那么多个thread 之间就会共享这个SimpleDateFormat,同时也是共享这个Calendar引用。单例、多线程、又有成员变量(这个变量在方法中是可以修改的),在高并发的情况下,容易出现幻读成员变量的现象,故说SimpleDateFormat是线程不安全的对象。

import java.text.SimpleDateFormat;import java.util.Date;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class DataFormatTest extends Thread {    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");    private String name;    private String dateStr;    public DataFormatTest(String name, String dateStr) {        this.name = name;        this.dateStr = dateStr;    }    @Override    public void run() {        try {            Date date = sdf.parse(dateStr);            System.out.println(name + ": date:" + date);        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws InterruptedException {        ExecutorService executorService = Executors.newFixedThreadPool(3);        executorService.execute(new DataFormatTest("A----->", "2022-02-01"));        // Thread.sleep(100);        executorService.execute(new DataFormatTest("B----->", "2022-02-31"));        executorService.shutdown();    }}

机器够好,每次运行都是错误:

java.lang.NumberFormatException: multiple points	at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1890)	at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)	at java.lang.Double.parseDouble(Double.java:538)	at java.text.DigitList.getDouble(DigitList.java:169)	at java.text.DecimalFormat.parse(DecimalFormat.java:2056)	at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1869)	at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1514)	at java.text.DateFormat.parse(DateFormat.java:364)	at com.test.DataFormatTest.run(DataFormatTest.java:23)	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)	at java.lang.Thread.run(Thread.java:745)

如果把休眠时间注释去掉,因为不再存在多线程并发问题,程序正常运行。


解决

1:将SimpleDateFormat定义成局部变量。

2:方法加同步锁synchronized,在同一时刻,只有一个线程可以执行类中的某个方法。

3:使用ThreadLocal:每个线程拥有自己的SimpleDateFormat对象。

4:使用DateTimeFormatter代替SimpleDateFormat。


DateTimeFormatter使用示例,多线程不会有问题

import java.time.LocalDateTime;import java.time.format.DateTimeFormatter;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class DataFormatTest2 extends Thread {    private static DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");    private static DateTimeFormatter dtf2=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");    private String name;    private String dateStr;    public DataFormatTest2(String name, String dateStr) {        this.name = name;        this.dateStr = dateStr;    }    @Override    public void run() {        try {            // 解析为日期            LocalDateTime localDateTime = LocalDateTime.parse(dateStr, dtf);            // 格式化日期            System.out.println(name + ": date:" + localDateTime.format(dtf));        } catch (Exception e) {            e.printStackTrace();        }    }    public static void main(String[] args) throws InterruptedException {        ExecutorService executorService = Executors.newFixedThreadPool(3);        executorService.execute(new DataFormatTest2("A----->", "2022/02/02 15:23:46"));        executorService.execute(new DataFormatTest2("B----->", "2022/02/22 15:23:46"));        executorService.shutdown();    }}


初始化一个LocalDateTime对象

Obtains an instance of LocalDateTime from year, month, day, hour, minute, second and nanosecond.This returns a LocalDateTime with the specified year, month, day-of-month, hour, minute, second and nanosecond. The day must be valid for the year and month, otherwise an exception will be thrown.Params:year – the year to represent, from MIN_YEAR to MAX_YEARmonth – the month-of-year to represent, from 1 (January) to 12 (December)dayOfMonth – the day-of-month to represent, from 1 to 31hour – the hour-of-day to represent, from 0 to 23minute – the minute-of-hour to represent, from 0 to 59second – the second-of-minute to represent, from 0 to 59nanoOfSecond – the nano-of-second to represent, from 0 to 999,999,999Returns:the local date-time, not nullThrows:DateTimeException – if the value of any field is out of range, or if the day-of-month is invalid for the month-yearpublic static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) {	LocalDate date = LocalDate.of(year, month, dayOfMonth);	LocalTime time = LocalTime.of(hour, minute, second, nanoOfSecond);	return new LocalDateTime(date, time);}


Date和LocalDateTime对象互转

// Date转LocalDateTimeDate date = new Date();LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());// LocalDateTime转DateZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());date = Date.from(zdt.toInstant());System.out.println(localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));


END

相关内容

文章评论

表情

共 0 条评论,查看全部
  • 这篇文章还没有收到评论,赶紧来抢沙发吧~

评论排行榜

热门标签