面試官:ThreadLocal的應用場景和注意事項有哪些?
阿新 • • 發佈:2020-04-05
## 前言
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 Thr