面试官:ThreadLocal的应用场景和注意事项有哪些?
2020-04-05 10:47
1341 查看
## 前言
ThreadLocal主要有如下2个作用
1. 保证线程安全
2. 在线程级别传递变量
## 保证线程安全
最近一个小伙伴把项目中封装的日期工具类用在多线程环境下居然出了问题,来看看怎么回事吧
日期转换的一个工具类
```java
public class DateUtil {
private static final SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static Date parse(String dateStr) {
Date date = null;
try {
date = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
```
然后将这个工具类用在多线程环境下
```java
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
service.execute(()->{
System.out.println(DateUtil.parse("2019-06-01 16:34:30"));
});
}
service.shutdown();
}
```
结果报异常了,因为部分线程获取的时间不对![在这里插入图片描述](https://img2020.cnblogs.com/other/748509/202004/748509-20200405104725851-804711471.png)
这个异常就不从源码的角度分析了,写一个小Demo,理解了这个小Demo,就理解了原因
一个将数字加10的工具类
```java
public class NumUtil {
public static int addNum = 0;
public static int add10(int num) {
addNum = num;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return addNum + 10;
}
}
```
```java
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
int num = i;
service.execute(()->{
System.out.println(num + " " + NumUtil.add10(num));
});
}
service.shutdown();
}
```
然后代码的一部分输出为
```text
0 28
3 28
7 28
11 28
15 28
```
什么鬼,不是加10么,怎么都输出了28?这主要是因为线程切换的原因,线程陆续将addNum值设置为0 ,3,7但是都没有执行完(没有执行到return addNum+10这一步)就被切换了,当其中一个线程将addNum值设置为18时,线程陆续开始执行addNum+10这一步,结果都输出了28。SimpleDateFormat的原因和这个类似,那么我们如何解决这个问题呢?
### 解决方案
**解决方案1:每次来都new新的,空间浪费比较大**
```java
public class DateUtil {
public static Date parse(String dateStr) {
SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
```
**解决方案2:方法用synchronized修饰,并发上不来**
```java
public class DateUtil {
private static final SimpleDateFormat sdf =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static synchronized Date parse(String dateStr) {
Date date = null;
try {
date = sdf.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
```
**解决方案3:用jdk1.8中的日期格式类DateFormatter,DateTimeFormatter**
```java
public class DateUtil {
private static DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static LocalDateTime parse(String dateStr) {
return LocalDateTime.parse(dateStr, formatter);
}
}
```
**解决方案4:用ThreadLocal,一个线程一个SimpleDateFormat对象**
```java
public class DateUtil {
private static ThreadLocal
相关文章推荐
- 悬浮窗口的应用和应用场景和注意事项
- COOKIE 应用的注意事项有哪些?
- 继承的概述及特点,方法重写的应用场景&注意事项
- zookeeper 有哪些应用场景?面试官希望听到这 4 点
- 牛客网Java刷题知识点之匿名对象、匿名对象的内存结构图、匿名对象的应用场景、匿名对象的使用、匿名对象的简单例子、匿名对象要注意的事项
- 开发中使用临时表的应用场景以及注意事项
- 函数声明和函数表达式的(区别)应用场景注意事项
- Block的基本使用、typedef给Block定义别名、Block的应用场景、Block的注意事项
- 关于Android应用开发的一些安全注意事项
- tcp_tw_reuse、tcp_tw_recycle 使用场景及注意事项
- 应用汉化过程遇到的问题 汉化必须注意的事项
- A_Prn_Barcode 应用code128码的 注意事项
- velocity与spring整合的配置、应用以及注意事项
- 上层应用调用自定义的系统服务注意事项
- 我们来讲下C#中的@符号的使用及注意事项。应用c#保留字
- CSS3代码编写中有哪些注意事项?
- linux下高并发网络应用注意事项
- linux下部署tomcat+mysql应用时的注意事项
- 在 Objective-C 中对 Block 应用 property 时的注意事项
- ViewState的原理,应用场合,注意事项