JUC:SimpleDateFormat的线程安全问题 以及 不可变类型DateTimeFormatter的使用

文章目录

  • 不可变类
    • SimpleDateFormat
      • 为什么不安全?
      • 解决
    • 不可变类保证线程安全的实现

不可变类

SimpleDateFormat

    public static void main(String[] args) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");for (int i = 0; i < 100; i++) {new Thread(() -> {try {System.out.println(simpleDateFormat.parse("1951-04-21"));} catch (ParseException e) {e.printStackTrace();}}).start();}}

SimpleDateFormat是一个线程不安全的,多线程下大概率出现报错:
在这里插入图片描述

为什么不安全?

来看看这个方法:

    @Overridepublic Date parse(String text, ParsePosition pos){checkNegativeNumberExpression();int start = pos.index;int oldStart = start;int textLength = text.length();boolean[] ambiguousYear = {false};CalendarBuilder calb = new CalendarBuilder();for (int i = 0; i < compiledPattern.length; ) {int tag = compiledPattern[i] >>> 8;int count = compiledPattern[i++] & 0xff;if (count == 255) {count = compiledPattern[i++] << 16;count |= compiledPattern[i++];}switch (tag) {case TAG_QUOTE_ASCII_CHAR:if (start >= textLength || text.charAt(start) != (char)count) {pos.index = oldStart;pos.errorIndex = start;return null;}start++;break;case TAG_QUOTE_CHARS:while (count-- > 0) {if (start >= textLength || text.charAt(start) != compiledPattern[i++]) {pos.index = oldStart;pos.errorIndex = start;return null;}start++;}break;default:// Peek the next pattern to determine if we need to// obey the number of pattern letters for// parsing. It's required when parsing contiguous// digit text (e.g., "20010704") with a pattern which// has no delimiters between fields, like "yyyyMMdd".boolean obeyCount = false;// In Arabic, a minus sign for a negative number is put after// the number. Even in another locale, a minus sign can be// put after a number using DateFormat.setNumberFormat().// If both the minus sign and the field-delimiter are '-',// subParse() needs to determine whether a '-' after a number// in the given text is a delimiter or is a minus sign for the// preceding number. We give subParse() a clue based on the// information in compiledPattern.boolean useFollowingMinusSignAsDelimiter = false;if (i < compiledPattern.length) {int nextTag = compiledPattern[i] >>> 8;if (!(nextTag == TAG_QUOTE_ASCII_CHAR ||nextTag == TAG_QUOTE_CHARS)) {obeyCount = true;}if (hasFollowingMinusSign &&(nextTag == TAG_QUOTE_ASCII_CHAR ||nextTag == TAG_QUOTE_CHARS)) {int c;if (nextTag == TAG_QUOTE_ASCII_CHAR) {c = compiledPattern[i] & 0xff;} else {c = compiledPattern[i+1];}if (c == minusSign) {useFollowingMinusSignAsDelimiter = true;}}}start = subParse(text, start, tag, count, obeyCount,ambiguousYear, pos,useFollowingMinusSignAsDelimiter, calb);if (start < 0) {pos.index = oldStart;return null;}}}// At this point the fields of Calendar have been set.  Calendar// will fill in default values for missing fields when the time// is computed.pos.index = start;Date parsedDate;try {parsedDate = calb.establish(calendar).getTime();// If the year value is ambiguous,// then the two-digit year == the default start yearif (ambiguousYear[0]) {if (parsedDate.before(defaultCenturyStart)) {parsedDate = calb.addYear(100).establish(calendar).getTime();}}}// An IllegalArgumentException will be thrown by Calendar.getTime()// if any fields are out of range, e.g., MONTH == 17.catch (IllegalArgumentException e) {pos.errorIndex = start;pos.index = oldStart;return null;}return parsedDate;}

在这里插入图片描述

主要是这里调用了establish()

  Calendar establish(Calendar cal) {boolean weekDate = isSet(WEEK_YEAR)&& field[WEEK_YEAR] > field[YEAR];if (weekDate && !cal.isWeekDateSupported()) {// Use YEAR insteadif (!isSet(YEAR)) {set(YEAR, field[MAX_FIELD + WEEK_YEAR]);}weekDate = false;}cal.clear();// Set the fields from the min stamp to the max stamp so that// the field resolution works in the Calendar.for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) {for (int index = 0; index <= maxFieldIndex; index++) {if (field[index] == stamp) {cal.set(index, field[MAX_FIELD + index]);break;}}}if (weekDate) {int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1;int dayOfWeek = isSet(DAY_OF_WEEK) ?field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek();if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) {if (dayOfWeek >= 8) {dayOfWeek--;weekOfYear += dayOfWeek / 7;dayOfWeek = (dayOfWeek % 7) + 1;} else {while (dayOfWeek <= 0) {dayOfWeek += 7;weekOfYear--;}}dayOfWeek = toCalendarDayOfWeek(dayOfWeek);}cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek);}return cal;}

在这里插入图片描述

参数为calender,那么这个canlender出自哪里呢?
在这里插入图片描述

实际上是继承DateFormat里的。

establish中先进行了cal.clear(),又进行了cal.set()。 先清除cal对象中是值,再设置新的值。

这样多线程下,调用同一个cal,而cal又没有设置线程安全,比如线程t1刚set完,t2进行了clear,那t1的输出肯定是不对的。

下面介绍几种解决方法。

解决

方法一
首先想到的肯定是加锁

public class test10 {public static void main(String[] args) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");for (int i = 0; i < 100; i++) {new Thread(() -> {synchronized (simpleDateFormat) {try {System.out.println(simpleDateFormat.parse("1951-04-21"));} catch (ParseException e) {e.printStackTrace();}}}).start();}}
}

在这里插入图片描述

这样可以解决,但是效率肯定是不高的。

方法二

利用不可变类型DateTimeFormatter
源码介绍是一个不可变的、线程安全的类
在这里插入图片描述

public class test10 {public static void main(String[] args) {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");for (int i = 0; i < 100; i++) {new Thread(() -> {TemporalAccessor parse = dateTimeFormatter.parse("1951-04-21");System.out.println(parse);}).start();}}
}

在这里插入图片描述

不可变类保证线程安全的实现

不可变类的类,属性都是final修饰的。
例如String:

在这里插入图片描述

保证只读和防止修改。

同时其方法都是操作一个新对象,并不在原对象上操作,保证了安全。
之前String都介绍过,这里就不再给出了,可以自己看看String的方法源码。
参考:
https://zhuanlan.zhihu.com/p/395751163

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/298252.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

get请求搜索功能爬虫

<!--爬虫仅支持1.8版本的jdk--> <!-- 爬虫需要的依赖--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.2</version> </dependency>…

MicroPython 树莓派 RP2 入门教程

系列文章目录 前言 Raspberry Pi Pico 开发板&#xff08;图片来源&#xff1a;Raspberry Pi 基金会&#xff09;。 以下是 Raspberry Pi RP2xxx 板的快速参考资料。如果您是第一次使用该开发板&#xff0c;了解微控制器的概况可能会对您有所帮助&#xff1a; 一、关于 RP2xxx…

C++ | Leetcode C++题解之第10题正则表达式匹配

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isMatch(string s, string p) {int m s.size();int n p.size();auto matches [&](int i, int j) {if (i 0) {return false;}if (p[j - 1] .) {return true;}return s[i - 1] p[j - 1];};vector<…

android 使用ollvm混淆so

使用到的工具 ndk 21.4.7075529&#xff08;android studio上下载的&#xff09;cmake 3.10.2.4988404&#xff08;android studio上下载的&#xff09;llvm-9.0.1llvm-mingw-20230130-msvcrt-x86_64.zipPython 3.11.5 环境配置 添加cmake mingw环境变量如下图: 编译 下载…

MySQL -- 07_最流行的查询需求分析(一些分组排序查询、开窗函数 dense_rank、distinct 去重函数 等~)

目录 最流行的查询需求分析07演示数据准备的SQL需求演示36、查询每一门课程成绩都在70分以上的姓名、课程名称和分数group by min() in() 函数 37、查询不及格的课程及学生普通表连接查询 38、查询课程编号为01语文且课程成绩在80分以上的学生的学号和姓名普通表连接查询 39、…

【国信华源2024年首场春季校园招聘面试会举办】

阳春三月&#xff0c;春意盎然&#xff0c;北京国信华源科技有限公司2024年校园招聘活动如期展开。4月2日&#xff0c;成功举办了“国信华源2024年首场春季校园招聘面试会”。 国信华源公司人力资源部热情接待了前来参加面试的同学们&#xff0c;并亲自陪同他们深入探访了企业。…

构建开源可观测平台

企业始终面临着确保 IT 基础设施和应用程序全年可用的压力。现代架构&#xff08;容器、混合云、SOA、微服务等&#xff09;的复杂性不断增长&#xff0c;产生大量难以管理的日志。我们需要智能应用程序性能管理 (APM) 和可观察性工具来实现卓越生产并满足可用性和正常运行时间…

【量子计算机为什么能吊打通用计算机】浅谈

Quntum Computer 一、量子计算机导入 这是一双手&#xff0c;这是大自然送给你最神奇的礼物&#xff0c;你用它来写字、吃饭、打游戏&#xff0c;除此之外&#xff0c;它还有一个妙不可言的功能&#xff0c;计算。是的&#xff0c;手是你人生中的第一个计算器&#xff0c;到小…

非关系型数据库(缓存数据库)redis的基础认知与安装

目录 一.关系型数据库和非关系型数据库 关系型数据库 非关系型数据库 关系数据库与非关系型数据库的区别 ①非关系数据 关系型数据库 非关系型数据库产生背景 数据存储流向 非关系型数据库 关系数据库 二.redis的简介 1.概念 2.Redis 具有以下几个优点: 3.Redi…

日期专题:做题笔记 (时间显示/星期计算/星系炸弹/第几天/纪念日)

目录 时间显示 代码 星期计算 代码 星系炸弹 代码 第几天 纪念日 代码 时间显示 时间显示 这道题主要是单位换算。 ①单位换算 ②输出格式&#xff1a; a. 不足两位补前导零。利用printf输出 b. 注意 long long 输出格式应该是 %lld 长整型 代码 #include <…

Coursera自然语言处理专项课程04:Natural Language Processing with Attention Models笔记 Week01

Natural Language Processing with Attention Models Course Certificate 本文是学习这门课 Natural Language Processing with Attention Models的学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 文章目录 Natural Language Processing with Attention ModelsWeek 01…

Maven POM元素解析

这是对Maven中使用的Maven项目描述符的引用。 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/…

Runes 生态一周要览 ▣ 2024.3.25-3.31|Runes 协议更新 BTC 减半在即

Runes 生态大事摘要 1、Casey 发布了 Runes 协议文档 RUNES HAVE DOCS&#xff0c;Github 代码库更新到 ord 0.17.0 版本&#xff0c;Casey 表示符文是一个“严肃”的代币协议。 2、Casey 公布了第一个硬编码的创世符文「UNCOMMONGOODS」 3、4月7日香港沙龙&#xff5c;聚焦「…

HTTPS跟HTTP有区别吗?

HTTPS和HTTP的区别&#xff0c;白话一点说就是&#xff1a; 1. 安全程度&#xff1a; - HTTP&#xff1a;就像是你和朋友面对面聊天&#xff0c;说的话大家都能听见&#xff08;信息明文传输&#xff0c;容易被偷听&#xff09;。 - HTTPS&#xff1a;就像是你们俩戴着加密耳机…

当代软件专业大学生与青年在新质生产力背景下的发展探究

在新质生产力的浪潮中,信息技术以前所未有的速度革新,为软件专业的大学生和青年带来了丰富的机遇,同时也伴随着一系列的挑战。他们如何把握时代的脉搏,实现个人的发展,成为了值得深入探讨的话题。 一、新质生产力背景下的机遇 随着新质生产力的不断发展,信息技术在各个领…

电商技术揭秘一:电商架构设计与核心技术

文章目录 引言一、电商平台架构概述1.1 架构设计原则与架构类型选择1.2 传统电商平台架构与现代化架构趋势分析 二、高并发处理与负载均衡2.1 高并发访问特点分析与挑战2.2 负载均衡原理与算法选择 三、分布式数据库与缓存技术3.1 分布式数据库设计与一致性考量3.2 缓存策略与缓…

Windows11下Docker使用记录(一)

Docker使用记录&#xff08;一&#xff09; 简单介绍Docker安装Docker 常用命令Docker 可视化Docker 使用GPU可视化rviz、gazebo 在进行ROS项目开发时&#xff0c;如果只有一台Windows电脑&#xff0c;我们可以考虑使用WSL或Docker来搭建ROS环境。在尝试了两种方式后&#xff0…

MySQL 优化及故障排查

目录 一、mysql 前置知识点 二、MySQL 单实例常见故障 故障一 故障二 故障三 故障四 故障五 故障六 故障七 故障八 三、MySQL 主从故障排查 故障一 故障二 故障三 四、MySQL 优化 1.硬件方面 &#xff08;1&#xff09;关于 CPU &#xff08;2&#xff09;关…

2024 抖音欢笑中国年(二):AnnieX互动容器创新玩法解析

本文基于24年抖音春节活动业务背景&#xff0c;介绍了字节跨端容器AnnieX在游戏互动套件上的探索&#xff0c;致力于提升容器在游戏互动场景的优化能力。 业务背景 AnnieX作为字节一方游戏统一容器&#xff0c;服务字节内部电商、直播、UG等跨端场景业务。在字节一方游戏互动场…

R语言技能 | 不同数据类型的转换

原文链接&#xff1a;R语言技能 | 不同数据类型的转换 本期教程 写在前面 今天是4月份的第一天&#xff0c;再过2天后再一次迎来清明小假期。木鸡大家是否正常放假呢&#xff1f; 我们在使用R语言做数据分析时&#xff0c;会一直对数据进行不同类型的转换&#xff0c;有时候…