java的SimpleDateFormat线程不安全出标题了,虚竹教你多种解决方案(2) ...

打印 上一主题 下一主题

主题 532|帖子 532|积分 1596

先自我先容一下,小编浙江大学结业,去过华为、字节跳动等大厂,现在阿里P7
深知大多数步伐员,想要提拔技能,往往是自己摸索成长,但自己不成体系的自学结果低效又漫长,而且极易碰到天花板技能停滞不前!
因此收集整理了一份《2024年最新网络安全全套学习资料》,初志也很简单,就是盼望能够帮助到想自学提拔又不知道该从何学起的朋友。






既有得当小白学习的零根本资料,也有得当3年以上履历的小伙伴深入学习提拔的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!
由于文件比较多,这里只是将部分目次截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、解说视频,而且后续会连续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)

正文

String dateString = simpleDateFormat.format(new Date());
try {
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}
}
}
}

由图可知,已经包管了线程安全,但这种方案不发起在高并发场景下使用,由于会创建大量的SimpleDateFormat对象,影响性能。
解决方案2:加锁:synchronized锁和Lock锁

加synchronized锁

SimpleDateFormat对象还是定义为全局变量,然后需要调用SimpleDateFormat进行格式化时间时,再用synchronized包管线程安全。
public class SimpleDateFormatDemoTest2 {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
public static void main(String[] args) {
//1、创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//2、为线程池分配任务
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
for (int i = 0; i < 10; i++) {
pool.submit(threadPoolTest);
}
//3、关闭线程池
pool.shutdown();
}
static class ThreadPoolTest implements Runnable{
@Override
public void run() {
try {
synchronized (simpleDateFormat){
String dateString = simpleDateFormat.format(new Date());
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
}
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}
}
}
}

如图所示,线程是安全的。定义了全局变量SimpleDateFormat,镌汰了创建大量SimpleDateFormat对象的消耗。但是使用synchronized锁,
同一时刻只有一个线程能执行锁住的代码块,在高并发的环境下会影响性能。但这种方案不发起在高并发场景下使用
加Lock锁

加Lock锁和synchronized锁原理是一样的,都是使用锁机制包管线程的安全。
public class SimpleDateFormatDemoTest3 {
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
//1、创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//2、为线程池分配任务
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
for (int i = 0; i < 10; i++) {
pool.submit(threadPoolTest);
}
//3、关闭线程池
pool.shutdown();
}
static class ThreadPoolTest implements Runnable{
@Override
public void run() {
try {
lock.lock();
String dateString = simpleDateFormat.format(new Date());
Date parseDate = simpleDateFormat.parse(dateString);
String dateString2 = simpleDateFormat.format(parseDate);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}finally {
lock.unlock();
}
}
}
}

由结果可知,加Lock锁也能包管线程安全。要留意的是,最后肯定要释放锁,代码里在finally里增加了lock.unlock();,包管释放锁。
在高并发的环境下会影响性能。这种方案不发起在高并发场景下使用
解决方案3:使用ThreadLocal方式

使用ThreadLocal包管每一个线程有SimpleDateFormat对象副本。如许就能包管线程的安全。
public class SimpleDateFormatDemoTest4 {
private static ThreadLocal threadLocal = new ThreadLocal(){
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
}
};
public static void main(String[] args) {
//1、创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//2、为线程池分配任务
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
for (int i = 0; i < 10; i++) {
pool.submit(threadPoolTest);
}
//3、关闭线程池
pool.shutdown();
}
static class ThreadPoolTest implements Runnable{
@Override
public void run() {
try {
String dateString = threadLocal.get().format(new Date());
Date parseDate = threadLocal.get().parse(dateString);
String dateString2 = threadLocal.get().format(parseDate);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
} catch (Exception e) {
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}finally {
//制止内存走漏,使用完threadLocal后要调用remove方法清除数据
threadLocal.remove();
}
}
}
}

使用ThreadLocal能包管线程安全,且服从也是挺高的。得当高并发场景使用
解决方案4:使用DateTimeFormatter代替SimpleDateFormat

使用DateTimeFormatter代替SimpleDateFormat(DateTimeFormatter是线程安全的,java 8+支持)
DateTimeFormatter先容 传送门:万字博文教你搞懂java源码的日期和时间相干用法
public class DateTimeFormatterDemoTest5 {
private static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(“yyyy-MM-dd HH:mm:ss”);
public static void main(String[] args) {
//1、创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//2、为线程池分配任务
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
for (int i = 0; i < 10; i++) {
pool.submit(threadPoolTest);
}
//3、关闭线程池
pool.shutdown();
}
static class ThreadPoolTest implements Runnable{
@Override
public void run() {
try {
String dateString = dateTimeFormatter.format(LocalDateTime.now());
TemporalAccessor temporalAccessor = dateTimeFormatter.parse(dateString);
String dateString2 = dateTimeFormatter.format(temporalAccessor);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
} catch (Exception e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}
}
}
}

使用DateTimeFormatter能包管线程安全,且服从也是挺高的。得当高并发场景使用
解决方案5:使用FastDateFormat 替换SimpleDateFormat

使用FastDateFormat 替换SimpleDateFormat(FastDateFormat 是线程安全的,Apache Commons Lang包支持,不受限于java版本)
public class FastDateFormatDemo6 {
private static FastDateFormat fastDateFormat = FastDateFormat.getInstance(“yyyy-MM-dd HH:mm:ss”);
public static void main(String[] args) {
//1、创建线程池
ExecutorService pool = Executors.newFixedThreadPool(5);
//2、为线程池分配任务
ThreadPoolTest threadPoolTest = new ThreadPoolTest();
for (int i = 0; i < 10; i++) {
pool.submit(threadPoolTest);
}
//3、关闭线程池
pool.shutdown();
}
static class ThreadPoolTest implements Runnable{
@Override
public void run() {
try {
String dateString = fastDateFormat.format(new Date());
Date parseDate = fastDateFormat.parse(dateString);
String dateString2 = fastDateFormat.format(parseDate);
System.out.println(Thread.currentThread().getName()+" 线程是否安全: "+dateString.equals(dateString2));
} catch (Exception e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName()+" 格式化失败 ");
}
}
}
}
使用FastDateFormat能包管线程安全,且服从也是挺高的。得当高并发场景使用
FastDateFormat源码分析

Apache Commons Lang 3.5
//FastDateFormat@Overridepublic String format(final Date date) { return printer.format(date);} @Override public String format(final Date date) { final Calendar c = Calendar.getInstance(timeZone, locale); c.setTime(date); return applyRulesToString©; }
源码中 Calender 是在 format 方法里创建的,肯定不会出现 setTime 的线程安全标题。如许线程安全迷惑解决了。那还有性能标题要考虑?
我们来看下FastDateFormat是怎么获取的
FastDateFormat.getInstance();FastDateFormat.getInstance(CHINESE_DATE_TIME_PATTERN);
看下对应的源码
/** * 获得 FastDateFormat实例,使用默认格式和地区 * * @return FastDateFormat /public static FastDateFormat getInstance() { return CACHE.getInstance();}/* * 获得 FastDateFormat 实例,使用默认地区
* 支持缓存 * * @param pattern 使用{@link java.text.SimpleDateFormat} 相同的日期格式 * @return FastDateFormat * @throws IllegalArgumentException 日期格式标题 */public static FastDateFormat getInstance(final String pattern) { return CACHE.getInstance(pattern, null, null);}
这里有用到一个CACHE,看来用了缓存,往下看
private static final FormatCache CACHE = new FormatCache(){ @Override protected FastDateFormat createInstance(final String pattern, final TimeZone timeZone, final Locale locale) { return new FastDateFormat(pattern, timeZone, locale); }};//abstract class FormatCache { … private final ConcurrentMap<Tuple, F> cInstanceCache = new ConcurrentHashMap<>(7); private static final ConcurrentMap<Tuple, String> C_DATE_TIME_INSTANCE_CACHE = new ConcurrentHashMap<>(7); …}

在getInstance 方法中加了ConcurrentMap 做缓存,提高了性能。且我们知道ConcurrentMap 也是线程安全的。
实践

/**


  • 年月格式 {@link FastDateFormat}:yyyy-MM
*/
public static final FastDateFormat NORM_MONTH_FORMAT = FastDateFormat.getInstance(NORM_MONTH_PATTERN);

给大家的福利

零根本入门
对于从来没有接触过网络安全的同学,我们帮你准备了具体的学习成长路线图。可以说是最科学最体系的学习路线,大家跟着这个大的方向学习准没标题。

同时每个成长路线对应的板块都有配套的视频提供:

因篇幅有限,仅展示部分资料
网上学习资料一大堆,但如果学到的知识不成体系,遇到标题时只是浅尝辄止,不再深入研究,那么很难做到真正的技能提拔。
需要这份体系化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)

一个人可以走的很快,但一群人才气走的更远!不论你是正从事IT行业的老鸟或是对IT行业感爱好的新人,都欢迎加入我们的的圈子(技能交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
t/5e9d8fabbff309ed788541717b2a4706.png)
给大家的福利

零根本入门
对于从来没有接触过网络安全的同学,我们帮你准备了具体的学习成长路线图。可以说是最科学最体系的学习路线,大家跟着这个大的方向学习准没标题。

同时每个成长路线对应的板块都有配套的视频提供:

因篇幅有限,仅展示部分资料
网上学习资料一大堆,但如果学到的知识不成体系,遇到标题时只是浅尝辄止,不再深入研究,那么很难做到真正的技能提拔。
需要这份体系化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
[外链图片转存中…(img-sHXg9b4C-1713336322711)]
一个人可以走的很快,但一群人才气走的更远!不论你是正从事IT行业的老鸟或是对IT行业感爱好的新人,都欢迎加入我们的的圈子(技能交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

干翻全岛蛙蛙

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表