大家好,我是哪吒。
你应该听过“时区”这个名词,大家也都知道,相同时刻不同时区的时间是不一样的。
因此在使用时间时,一定要给出时区信息。
对于当前的上海时区和纽约时区,转化为 UTC 时间戳是不同的时间。
对于同一个本地时间的表示,不同时区的人解析得到的 UTC 时间一定是不同的,反过来不同的本地时间可能对应同一个 UTC。
我当前时区的 Offset(时差)是 +8 小时,对于 +9 小时的纽约,整整差了1个小时,北京早上 10 点对应早上东京 11 点。
Java 8 推出了新的时间日期类 ZoneId、ZoneOffset、LocalDateTime、ZonedDateTime 和 DateTimeFormatter,处理时区问题更简单清晰。
- Asia/Shanghai对应+8,对应2023-11-10 10:00:00。
- Asia/Tokyo对应+9,对应2023-11-10 11:00:00。
- timeZone 是+2,所以对应2023-11-10 04:00:00。
- 通过ZoneId,定义时区;
- 使用ZonedDateTime保存时间;
- 通过withZone对DateTimeFormatter设置时区;
- 进行时间格式化得到本地时间;
思路比较清晰,不容易出错。
百度一下,才知道是高并发情况下SimpleDateFormat有线程安全的问题。
下面通过模拟高并发,把这个问题复现一下:
果不其然,报错。还将2023年转换成2220年,我勒个乖乖。
在时间工具类里,时间格式化,我都是这样弄的啊,没问题啊,为啥这个不行?原来是因为共用了同一个SimpleDateFormat,在工具类里,一个线程一个SimpleDateFormat,当然没问题啦!
可以通过TreadLocal 局部变量,解决SimpleDateFormat的线程安全问题。
- 先new CalendarBuilder()。
- 通过parsedDate = calb.establish(calendar).getTime();解析时间。
- establish方法内先cal.clear(),再重新构建cal,整个操作没有加锁。
上面几步就会导致在高并发场景下,线程1正在操作一个Calendar,此时线程2又来了。线程1还没来得及处理 Calendar 就被线程2清空了。
因此,通过编写Date工具类,一个线程一个SimpleDateFormat,还是有一定道理的。